Dynamic Fog Color

Help bring our custom Ages to life! Share tips and tricks, as well as code samples with other developers.

Dynamic Fog Color

Postby D'Lanor » Tue Nov 13, 2007 8:28 am

Dynamic Fog Color

This Python code is based on my old adminKI contribution The Sky Blender. These are instructions to properly implement it into an age.
The Python method PtFogSetDefColor() has a low performance footprint (as opposed to animating objects with Python).
Those who are familiar with the old adminKI code may worry about the strain this would put on the server. However, that only happened because we had to push the changes to the other clients. With this new multiplayer compatibility all clients handle the changes internally and they only need to sync themselves occasionally.

Multiplayer compatibility: Color states are periodically written to SDL variables by only one player who is the game owner. The game owner is usually the first player who arrives in the age.
Players who link in will retrieve the SDL variables and their loop will start from the current values. Age players are synced to the game owner every 30 seconds. If the game owner links out (or crashes!) another age player becomes the game owner.

The game owner mechanism: All you have to do is check the isLocallyOwned() property of an object in your age. If this check returns True then you are the game owner. This test object does not have to be a special object, just pick one at random. Uru usually puts the test object in a PythonFileMod but it works just as well with PtFindSceneobject(), which is much easier to implement. Thanks to a'moaca' for pointing out this mechanism.

SDL Variables: Add these variables to SDL file for your age. Replace the words between ### to match your age.

Code: Select all
#
# State Description Language for ###YourAgeName###
#

STATEDESC ###YourAgeName###
{
   VERSION 1

   VAR FLOAT    CurrColR[1]      DEFAULT=0.5
   VAR FLOAT    CurrColG[1]      DEFAULT=0.5
   VAR FLOAT    CurrColB[1]      DEFAULT=0.5
   VAR BOOL    ColStateR[1]      DEFAULT=1
   VAR BOOL    ColStateG[1]      DEFAULT=1
   VAR BOOL    ColStateB[1]      DEFAULT=1
}


Python Code: Add the Python code to your main Python agehook file (or create a custom PythonFileMod for it if you want). Adjust the ColRate, CurrCol and kLoopTime settings to get different effects.
Make sure to turn on fog in the *.fni file for your age. If you don't have fog you will not see anything! Or you can uncomment the line PtFogSetDefLinear(-50, 600, 1) to add a Kadish Tolesa type fog to the age.

Optional: Match the ClearColor. This option is off by default but the effect generally looks better when it is on. Calling the function with self.ColorBlender(ColRate, 1) turns it on.

Replace the words between ### to match your age.

Code: Select all
##################################
#                                #
#  Dynamic Fog Color by D'Lanor  #
#                                #
##################################

from Plasma import *
from PlasmaTypes import *
ColState = [1, 1, 1]
CurrCol = [0.5, 0.5, 0.5]
ColRate = [0.3, 0.6, 0.9]
LoopState = 0
kLoopTime = 0.5
kLoopID = 1
DoUpdate = 0
kUpdateTime = 30
kUpdateID = 2
testObj = None
class ###YourAgeName###(ptResponder,):

    def __init__(self):
        ptResponder.__init__(self)
        self.version = '###Your Version Number###'
        print ('__init__%s v.%s' % (self.__class__.__name__,
         self.version))



    def OnFirstUpdate(self):
        print ('%s: OnFirstUpdate called' % self.__class__.__name__)
        #PtFogSetDefLinear(-50, 600, 1)



    def OnServerInitComplete(self):
        global LoopState
        global DoUpdate
        global testObj
        print ('%s: OnServerInitComplete called' % self.__class__.__name__)
        try:
            testObj = PtFindSceneobject('###YourObjectName###', PtGetAgeName())
        except:
            print 'ERROR: Missing test object'
            return
        ageSDL = PtGetAgeSDL()
        if (ageSDL == None):
            print 'ERROR: Missing SDL'
            return
        ageSDL.sendToClients('CurrColR')
        ageSDL.sendToClients('CurrColG')
        ageSDL.sendToClients('CurrColB')
        ageSDL.sendToClients('ColStateR')
        ageSDL.sendToClients('ColStateG')
        ageSDL.sendToClients('ColStateB')
        ageSDL.setFlags('CurrColR', 1, 1)
        ageSDL.setFlags('CurrColG', 1, 1)
        ageSDL.setFlags('CurrColB', 1, 1)
        ageSDL.setFlags('ColStateR', 1, 1)
        ageSDL.setFlags('ColStateG', 1, 1)
        ageSDL.setFlags('ColStateB', 1, 1)
        ageSDL.setNotify(self.key, 'CurrColR', 0.0)
        ageSDL.setNotify(self.key, 'CurrColG', 0.0)
        ageSDL.setNotify(self.key, 'CurrColB', 0.0)
        ageSDL.setNotify(self.key, 'ColStateR', 0.0)
        ageSDL.setNotify(self.key, 'ColStateG', 0.0)
        ageSDL.setNotify(self.key, 'ColStateB', 0.0)
        CurrCol[0] = ageSDL['CurrColR'][0]
        CurrCol[1] = ageSDL['CurrColG'][0]
        CurrCol[2] = ageSDL['CurrColB'][0]
        ColState[0] = ageSDL['ColStateR'][0]
        ColState[1] = ageSDL['ColStateG'][0]
        ColState[2] = ageSDL['ColStateB'][0]
        LoopState = 1
        DoUpdate = 1
        PtAtTimeCallback(self.key, kLoopTime, kLoopID)
        PtAtTimeCallback(self.key, kUpdateTime, kUpdateID)



    def BeginAgeUnLoad(self, avatar):
        global LoopState
        global DoUpdate
        print ('%s: BeginAgeUnLoad called' % self.__class__.__name__)
        try:
            LocAvi = PtGetLocalAvatar()
        except:
            print 'ERROR: Local avatar has left the building'
            return
        if (LocAvi == avatar):
            LoopState = 0
            DoUpdate = 0



    def OnSDLNotify(self, VARname, SDLname, playerID, tag):
        print ('%s: OnSDLNotify: Varname = %s, playerID = %d' % (self.__class__.__name__,
         VARname, playerID))
        if (playerID != PtGetLocalPlayer().getPlayerID()):
            print 'Syncing color state to SDL'
            ageSDL = PtGetAgeSDL()
            if (VARname == 'CurrColR'):
                CurrCol[0] = ageSDL['CurrColR'][0]
            elif (VARname == 'CurrColG'):
                CurrCol[1] = ageSDL['CurrColG'][0]
            elif (VARname == 'CurrColB'):
                CurrCol[2] = ageSDL['CurrColB'][0]
            elif (VARname == 'ColStateR'):
                ColState[0] = ageSDL['ColStateR'][0]
            elif (VARname == 'ColStateG'):
                ColState[1] = ageSDL['ColStateG'][0]
            elif (VARname == 'ColStateB'):
                ColState[2] = ageSDL['ColStateB'][0]



    def OnTimer(self, id):
        if (id == kLoopID):
            if (not LoopState):
                print 'Stop color blender'
                return
            self.ColorBlender(ColRate, 1)
        if (id == kUpdateID):
            if (not DoUpdate):
                print 'Stop updating color state'
                return
            ageSDL = PtGetAgeSDL()
            if testObj.isLocallyOwned():
                print 'I am game owner. Updating color state'
                ageSDL['CurrColR'] = (CurrCol[0],)
                ageSDL['CurrColG'] = (CurrCol[1],)
                ageSDL['CurrColB'] = (CurrCol[2],)
                ageSDL['ColStateR'] = (ColState[0],)
                ageSDL['ColStateG'] = (ColState[1],)
                ageSDL['ColStateB'] = (ColState[2],)
            PtAtTimeCallback(self.key, kUpdateTime, kUpdateID)



    def ColorBlender(self, ColRate, MatchClear = 0):
        global LoopState
        try:
            ColRateR = abs(ColRate[0])
            ColRateG = abs(ColRate[1])
            ColRateB = abs(ColRate[2])
        except:
            PtDebugPrint('Invalid color value', level=kDebugDumpLevel)
            LoopState = 0
            return
        if ((ColRateR >= 100) or ((ColRateG >= 100) or (ColRateB >= 100))):
            PtDebugPrint('Value must be lower than 100', level=kDebugDumpLevel)
            LoopState = 0
            return
        if (ColState[0] == 0):
            ColRateR = (ColRateR * -1)
        if (ColState[1] == 0):
            ColRateG = (ColRateG * -1)
        if (ColState[2] == 0):
            ColRateB = (ColRateB * -1)
        CurrCol[0] = (CurrCol[0] + ((ColRateR / 100) * CurrCol[0]))
        CurrCol[1] = (CurrCol[1] + ((ColRateG / 100) * CurrCol[1]))
        CurrCol[2] = (CurrCol[2] + ((ColRateB / 100) * CurrCol[2]))
        if (CurrCol[0] >= 1):
            CurrCol[0] = 1.0
            ColState[0] = 0
        elif (CurrCol[0] < 0.01):
            ColState[0] = 1
        if (CurrCol[1] >= 1):
            CurrCol[1] = 1.0
            ColState[1] = 0
        elif (CurrCol[1] < 0.01):
            ColState[1] = 1
        if (CurrCol[2] >= 1):
            CurrCol[2] = 1.0
            ColState[2] = 0
        elif (CurrCol[2] < 0.01):
            ColState[2] = 1
        try:
            ColNew = ptColor(CurrCol[0], CurrCol[1], CurrCol[2], 1.0)
            PtFogSetDefColor(ColNew)
            if MatchClear:
                PtSetClearColor(CurrCol[0], CurrCol[1], CurrCol[2])
            PtAtTimeCallback(self.key, kLoopTime, kLoopID)
        except:
            PtDebugPrint('Something terribly has gone wrong, stop the loop!', level=kDebugDumpLevel)
            LoopState = 0

Example of the code in action: http://www.youtube.com/watch?v=vFopU-h9tUg

Feel free to use and adapt for your age creations.

D'Lanor
"It is in self-limitation that a master first shows himself." - Goethe
User avatar
D'Lanor
 
Posts: 1980
Joined: Sat Sep 29, 2007 4:24 am

Re: Dynamic Fog Color

Postby Chacal » Thu Nov 15, 2007 6:02 pm

Sounds great! I wish I had been active at the time, I would have delved head-first into Python in general and the adminKI in particular. I was thinking about setting up a shard when UU closed :(

I'd love to read some technical background about how the client communicates with the server, some historic background about the adminKI (how all this was discovered, how it worked, etc).
Also, can this technique be used to change the lighting in an Age?
For example, (hope it's not too off-topic), how could I change the light level and/or colour in the city?
Chacal


"The weak can never forgive. Forgiveness is an attribute of the strong."
-- Mahatma Gandhi
User avatar
Chacal
 
Posts: 2508
Joined: Tue Nov 06, 2007 2:45 pm
Location: Quebec, Canada

Re: Dynamic Fog Color

Postby Tweek » Thu Nov 15, 2007 8:39 pm

That's sweet. One of the things I like about WoW is you can be walking a long a nice bright forest and slowly the light will get darker and become a muddy green as the trees become more rotten. Being able to do something like that in Ages would be cool.
Beneath - IC Blog.
Beneath: Ages of Tweek - FB Age Dev Page.
User avatar
Tweek
 
Posts: 692
Joined: Sat Sep 29, 2007 6:37 am

Re: Dynamic Fog Color

Postby Aloys » Thu Nov 15, 2007 8:52 pm

Ooh, I remember wondering about that possibility on AB a while ago (maybe even a year ago), I was surprised this technique wasn't used in any Cyan Ages. (or maybe I just never noticed) It's great to see it's possible..
That will be very useful in some Ages. Walking outside in a nice clear blue fog then entering a cavern and being suddenly surrounded by a thick dark fog..

Is it possible to update the fog values using regions?
User avatar
Aloys
 
Posts: 1968
Joined: Sun Oct 21, 2007 7:57 pm
Location: France (GMT +1)

Re: Dynamic Fog Color

Postby Nadnerb » Thu Nov 15, 2007 9:16 pm

well, I would say that it is possible to use regions, because this effect is in fact used in a Cyan age or two. The fog thickens with distance in Minkata, as well as going away inside caves.

Whether you can use a region to trigger a script like this with our current tools, I'm not sure, but I can't imagine it being too hard. ;)
Image
Live KI: 34914 MOULa KI: 23247 Gehn KI: 11588 Available Ages: TunnelDemo3, BoxAge, Odema
Nadnerb
 
Posts: 1057
Joined: Fri Sep 28, 2007 8:01 pm
Location: US (Eastern Time)

Re: Dynamic Fog Color

Postby Lontahv » Fri Mar 21, 2008 10:20 pm

Yes, you can light the city lake up. I've done it.

Use this code:

http://forum.guildofwriters.com/viewtopic.php?f=9&p=14436#p14436

~Lontahv
Currently getting some ink on my hands over at the Guild Of Ink-Makers (PyPRP2).
User avatar
Lontahv
Councilor of Artistic Direction
 
Posts: 1331
Joined: Wed Oct 03, 2007 2:09 pm

Re: Dynamic Fog Color

Postby Chacal » Sat Mar 22, 2008 10:01 pm

I see. You can brighten up the lake, or any other light source in the city.
Will it brighten the city itself, or is all of it texture-painted?
Chacal


"The weak can never forgive. Forgiveness is an attribute of the strong."
-- Mahatma Gandhi
User avatar
Chacal
 
Posts: 2508
Joined: Tue Nov 06, 2007 2:45 pm
Location: Quebec, Canada

Re: Dynamic Fog Color

Postby Lontahv » Sat Mar 22, 2008 11:25 pm

Well, first you brighten the .fni(brightens the lake), and then you brighten the main lamps--in the harbor prp. It seems almost ALL the objects in the city light up. The vert-paint by no means is non-erasable by a bright lamp--this is why the avatars and ages like Gira are painted black, it's so they have a dark starting value--the lights brighten things up. Also, a fun thing to try is parenting a lamp to yourself and running around the city(it's like a flashlight).

Object parenting code:

Code: Select all
from Plasma import *

avatar = PtGetLocalAvatar()
AgeName = PtGetAgeName()
obj = PtFindSceneobject("<objName>", AgeName)
PtAttachObject(obj, avatar)


and to detach:

Code: Select all
avatar = PtGetLocalAvatar()
AgeName = PtGetAgeName()
obj = PtFindSceneobject("<objName>", AgeName)
PtDetachObject(obj, avatar)



~Lontahv
Currently getting some ink on my hands over at the Guild Of Ink-Makers (PyPRP2).
User avatar
Lontahv
Councilor of Artistic Direction
 
Posts: 1331
Joined: Wed Oct 03, 2007 2:09 pm

Re: Dynamic Fog Color

Postby Chacal » Sat Mar 22, 2008 11:53 pm

Oooo this was what we used in the userKI wasn't it? I forget the command.
Chacal


"The weak can never forgive. Forgiveness is an attribute of the strong."
-- Mahatma Gandhi
User avatar
Chacal
 
Posts: 2508
Joined: Tue Nov 06, 2007 2:45 pm
Location: Quebec, Canada

Re: Dynamic Fog Color

Postby Lontahv » Sun Mar 23, 2008 4:02 am

Err, I'm not sure if it was in the userKI. I don't think so. AdminKI on the otherhand.... ;)

Just pm me--I have some fun commands in a KI-compilation--and some instructions too. :)

The UserKI used the internal light--pre-stuck-to-avvie, this is even more powerful. You can more walls and barriers away with the parenting code--I also have a real-time object de-collision command(useful for opening up D'ni on-the-fly :) ).


~Lontahv
Currently getting some ink on my hands over at the Guild Of Ink-Makers (PyPRP2).
User avatar
Lontahv
Councilor of Artistic Direction
 
Posts: 1331
Joined: Wed Oct 03, 2007 2:09 pm

Next

Return to Scripting

Who is online

Users browsing this forum: No registered users and 0 guests

cron