Thursday, February 23, 2012

On Placement, Size, and Shape of LightingBalls and LightingBallControllers



LightingBalls: NO.  Size does not matter at all.  While LightingBalls are technically a trigger, their script does not have either an OnTriggerEnter or OnTriggerLeave Event.  So, the bounds on the primitive shape is purely aesthetic.  Make them as small or large as you want them to be.

LightingBallControllers: YES.  Size does matter for all three dimensions.
A typical LightingBallController.  You can imagine its balls placed in front and behind it.
Height:  I would not use anything less than 196 for height, which should pretty much always be on the z-axis, because a player can jump over one that is only 128 units high.  I believe 196 is pretty safe to use.

Width: I consider 64 to be generally safe for width.  The lower you go, the more aggressive the controller becomes.  So, in short corridors, you may feel its necessary to use a shorter width in order to make sure a light comes on before the player can see it happen.  With this potential payoff comes greater risk, however, as I will explain in great detail...
A top-view of the Playerball 1, and ball 2 forming a triangle with sides a, b, c; where ball 1 is always the closest ball to the player.  

Just after the player leaves the controller's boundary (moving from yellow to black), the controller's OnTriggerLeave Event fires and the calculations to determine which side the player is on begins -- the lengths of b and c are reported to the controller, which then compares the two.

After determining b is the shortest length,  ball 2's lights are disabled, while ball 1's lights are enabled.  However, some time does pass between the firing of the OnTriggerLeave Event and the actual enabling/disabling of lights.

Exactly how much time passes between these two events is not set in stone, and is not affected by the lengths of a, b, or c -- only by the amount of other things competing for processing time at that moment.  During this period of time, the player is still free to move about and do unanticipated things.   The width of the controller is the buffer zone where the players movement does not matter.

The Horizon Line (Yes, I totally made that up) is where b = c.  Thankfully, in this ideal setup, it is fairly deep within the controller.  As you shrink the width, the buffer zone between the horizon line and the controller's boundaries also shrinks -- but the position of the Horizon Line would not change.

Length:  Typically, the length of the controller needs to be long enough to completely cover all possible paths the player may take.  Whether its a narrow corridor, across an entire room,  or across half the cell... it doesn't matter.  It'll still work, because no matter how long b gets, it will always be significantly shorter than c.

 Placement

LightingBalls: YES.  Ball placement determines where the Horizon line actually is.



Here is an example of badly lopsided (hung?) balls.

Because line a does not form a right angle with the controller's boundaries, the Horizon Line is completely messed up (technical term).

This controller will not function as expected.  Or, more precisely:

There are very few paths that the player can take where the controller will function properly.

If the player runs from the top to the bottom, s/he would have to exit at a yellow section of the controller.  Conversely, if the player runs the other way, s/he would have to exit at a gold section of the controller. Otherwise, the wrong set of lights will go on/off.

For the Player's Path in the above image, c will always less than b.  Therefore, ball 2 will always be closest, and so the lights rigged to ball 1 will always be off.  And since you probably never bothered to test your mod before uploading it, people will complain about how buggy your mod is.  You'll end up blaming me, your mom, and your dog.

To fix it, you'd need to move either ball 1 or ball 2 to the left or right (respectively) so that line a is completely vertical in this image.  Alternatively, you could rotate the controller so that the horizon line is both parallel and bisects it.


You may be saying, "OK, Captain Obvious.  I understood Pythagoras's Theorum since I was like 8 months old."

Well, there are implications which you may not have considered, which I'll talk about later in an advanced tutorial just for you.

LightingBallControllers:  YES.  Placement  does matter.

Shape

LightingBalls: NO.  Shape does not matter at all.
LightingBallControllers: YES.  Shape does matter.

<I'll finish this later>

Go back to main tutorial page

How to Create LightingBalls and LightingBallControllers: Step by Step Tutorial

Go back to main tutorial page


The starting point for the tutorial is as such:

A) You have already downloaded and extracted to your game's data folder version  1.1 (or newer) of the Just In Time Lighting lighting scripts.

B) You have already opened a cell, and are looking at the area in the render window where you'd like to create your LightingBalls and LightingBallController.

C) (Optional but recommended) You have "Statics" excluded from Render Window Picking Preferences in the Edit pulldown.

My screen at this point.






















This may seem like a lot of steps, but that's only because I've broken it down Barney style for you.  Also, once you've created a group of objects, it's simple to copy and paste them around.


1) In the Object Window, go to World Objects > Activators.

2) Create a new Activator, and in the ID field type in: LightingBall

3) Choose its color.  I prefer blue.



4) Click on the OK button.

5) Type the word "lighting" into the filter box in the Object Window, and right-click on the new LightingBall base object in that window.  Then select Edit.

6) Click on the Add button in the Scripts section, then find and select LightingBall from the list of scripts.

7) Click on OK to select the script, and then OK again to close the Activator window.

8)  Repeat steps 2 through 7, except this time for the LightingBallController.  I will use yellow for their color.

9) Click on the "Create Trigger Button".









10) In the Select Form window that pops up, go ahead and type the word "lighting" into the filter box.




11) We'll make a LightingBallController first, so select that and click OK, and then hit shift+F to center the new LightingBallController in your Render window.

12) the Cell View window, go type "lighting" into the filter box.  Right-click the LightingBallController in this same window, and then select and Edit.

13) Click on the Primitive Tab, and edit the Reference Editor ID and Bounds data so that it looks like this:



14) Click OK, and position it somewhere that makes it easy to work with.


15) With LightingBallController still selected, go ahead and repeat steps 9 through 12, except this time for a LightingBall.  Don't worry, I'll wait.

16) Click on the Primitive Tab, and edit the  Reference Editor IDPrimitive Type, and Bounds data so that it looks like this:


17) Click OK, and it should already be positioned directly in the center of LightingBallControllerA.

18) With LightingBallA1 still selected, hit ctrl-D to duplicate it.  Now edit its RefID to be LightingBallA2, and click OK.

19) Say NO to the "Create new Form?" question... and YES, you're sure.

20) Go to Edit > Preferences, and set your Arrow speed to a sane value like so:



21) With LightingBallA2 still selected, go ahead use the arrow keys to move one of the balls so it starts looking like the picture below.

22) Do the same with LightingBallA1, except this time in the opposite direction.  It should look something like this when you're done:



Does it  need to be perfect?  No, definitely not.  But I'm not going to half-ass it in a tutorial... so to get it perfect, we'll open up both Edit windows for the balls at the same time and line them up by copy and pasting.



I would definitely at least put the balls on the same position on the z-axis (except for stairs, which probably should get their own tutorial section), and at least attempt to have each ball kinda the same distance from the edges of the controller's box.  At least try to make the line drawn between the 2 balls cut the controller to look like it at least might form a right angle.

As you approach perfection, the shorter the minimum controller width needs to be.

If you'd like to know more about this, click here.

23)  Open the Edit window for LightingBallControllerA, and click the little right arrow button a bunch of times until you see the Script tab.




24) Click on Edit Properties (or just hit the Properties button).  Then select ball_1.


25)  Click on the Edit Value button to the right, and then click on the Pick Reference in Render Window button.  Select LightingBallA1, and then repeat the process for ball_2 and selecting LightingBallA2.  No, it doesn't really matter if the names don't match.  However, that would drive me nuts.



You're done!  Kinda.  You have rigged 2 balls to your controller, but you'll still need to rig at least one light to each ball (You can have one ball not control any lights by setting its specialCaseNoDisables and specialCaseNoEnables boolean properties to True).

The process of rigging lights to the balls is the same as rigging the balls to the controller, so I think you got it from here.

To move the whole group around, you just select them all by holding down the ctrl key and clicking them (preferably in the Cell View window).

Remember, CTRL-D duplicates your selection, and it works even when you have multiple objects selected.

Copy and Paste works, too, of course.


Go back to main tutorial page

Wednesday, February 22, 2012

JIT LIGHTING - Skyrim CK Tutorial - Lights on Timers

Go back to main tutorial page

In version 1.1 I introduced a new feature... timed lighting.  It's now possible to using LightingBalls to control when a light, or group of lights, turns on/off.  Ok, the video itself isn't all that great (I'll replace it eventually).


The above video is an example of a 2-stage day/night cycle.  A 4-stage would be: dawn/day/dusk/night -- certainly, that would be even more impressive.

There is no limit to the number of stages you can have with the Just In Time Lighting scripts.

Futhermore, it can all work off of a single onUpdate() call... and you get to control the frequency at which it does the updates.

It gets even better... You can combine them with special triggers, so it only updates when the player crosses certain thresholds.  In that case, it works without registering for OnUpdate calls at all.

Or, you can do a mixture of both... where it will auto-update when the player is nearby (registering its OnUpdate event), but then stop when the player crosses a boundary (deregistering).  Yeah, this is more complicated.


I'm going to go through 4 examples, in increasing order of complexity...

Example 1:  As simple as it gets.

A is a LightingBall, and 1 is light we want to put on a timer -- which can optionally be tied to as many lights as you want via the "Enable Parent" tab.


























So, here we have LightingBall A (in blue), and Light #1.  Once we have the light rigged to the ball, all we need to do is go into the ball's script properties window, and:


a) Set OnTimer to TRUE

b) Set OnTimerUpdate to a value larger than 0.  This will be the interval at which it checks the current time.  We'll use 17, since I like prime numbers.

c) Set OnTimerBeginTime and OnTimerEndTime to be the range of time you'd want the light to be on (military time, kinda).  We'll use 8 and 20, respectively.


That's it, you're done.  The light will come on at 8 AM, and turn off at 8 PM.  While the player is in the cell, it will check to see if it needs to turn itself on/off once every 17 seconds of real-time.  If we wanted it to come on at 8:30 AM, we would use 8.5 for the start time (yes, 8.5, not 8.3).  Midnight would be 0 (not 24!!!).

While you can only have 4 lights to one LightingBall, you can work around this by setting other lights' "Enable Parent" to the light that is controlled by a LightingBall, or by using more balls -- whatever works for you.

Advanced:  Nothing's stopping you from using these scripts to put any object on a timer... it doesn't have to be a light source.

Example 2:  Two lights, two different times, one OnUpdate() event.  No problem.

A, B, and C are LightingBalls, while 1 and 2 are lights (or light groups).
























A, B, and C are all LightingBalls...  Of course, A is special because it's red.  In this example, only A is calling onUpdate(), but B and C will both turn on their respective lights at totally different times (as opposed to merely opposite times, which could be accomplished by using the layout from example 1, "Enable Parent", along with the "opposite" setting).

Just as in Example #1, without a controller you're letting the engine handle if these flicker or cause other lights to flicker.

Assuming they're all rigged together as shown, here's what we need to do (starting with ball A):


a) Set OnTimer to TRUE.
b) Set OnTimerUpdate to a value larger than 0. Again,  we'll use 17.

Now moving on to B:

a) Set OnTimer to TRUE.
b) Set OnTimerBeginTime and OnTimerEndTime to the range of time you want.  We'll use 8 and 20, like last time.

Finally, we'll modify C's script properties:

a) Set OnTimer to TRUE.
b) Set OnTimerBeginTime and OnTimerEndTime to 19 and 9, respectively.


That's it, you're done.  Now, light 1 will come on at 8 AM, and shut off at 8 PM... and light 2 will come on at 7 PM, and shut off at 9 AM.  So, there's 2 hours of overlap each day when both lights are on.  I don't know why you'd want to do that, but you can.  I know... I hate unrealistic examples, too.

While it's true that we could have done it using a separate onUpdate() timer for each light, sometimes it's necessary to have everything on the same timer.  Granted, I don't think that's true in this particular example, but whatever. ;) Again, you can think of light 1 and 2 more like light groups, rather than just individual lights.


Example 3:   Example 2, except also with a controller.


While the setup in examples 1 & 2 is useful in the general sense that it does what it's supposed to (have lights on timers), it does not really help help you add more dynamic lights to a cell... because one (or both) of the lights is or can be on without consideration to player's position in the cell.

So, here we have a controller (F, with its balls d & e) which turns both lights off when the player crosses its lower boundary, and lets them turn on when s/he crosses the upper boundary.  We're fortunate that in this example there's a wall that completely breaks the player's line of sight of all shadow casting lights and any non-shadow casting lights which may be Parent Enabled by them.  Such is not always the case (as in the next example).

Assuming they're all rigged together as shown, here's what we need to do:

A, B, and C are configured just as in example #2.


That's it, you're done!  Well, let's talk about the controller F, and it's balls d and e.

Nothing special is going on with F, d, or e.  Ball A is rigged to Ball d  just as if A was another light rigged to d.  Of course it's not a light, it's a LightingBall, but the script is built to take this into consideration.  Ball e probably has some other lights rigged to it, but if it doesn't then both the specialCaseNoEnables and specialCaseNoDisables flags must be set to TRUE.  Otherwise, my debugging code will assume you forgot to rig the lights on ball e, and it will complain at you when the cell loads (it's a feature).

So, if the controller turns everything on/off, and balls C & D have the time information... why do we need Ball A at all?

Because we want the lights to be able to update even if the player just stands in the room all day.


Example 4:  Example ESP Helgen Keep's Main Room Day/Night cycle setup demystified.






















Congratulations on making it this far!  This is a challenging problem due to there not being a wall handy to break line of sight of the entire room.  So, we have to fake it as best we can.

Lights 1 & 2 are non-shadow casting lights, while 3 & 4 do cast shadows.  1 & 3 are both on at the same timer, as well as 2 & 4.  Both timer pairs are on at opposite times.  There's 5 dynamic lights at the other end of the long hallway (not shown), so the controller is needed.

The non-shadow casting lights, when combined with the long view distance, help to create the illusion that the other lights are on -- even when they are not.  The trick is to let the engine handle the disabling of the non-shadow casting lights while still having them receive enable commands from the controller.  The reason the trick works is because the engine is surprisingly good at fading non-shadow casting lights from view.

Generally, you always want to let the engine handle non-shadow casting lights.

And that's the purpose of ball B.  It blocks any disable commands received through the chain from the X controller's ball y, because I set its specialCaseNoDisables flag to TRUE.   E & F contain the timer information (they are mirrors of C & D in that respect).

However, we do want the enable commands from ball y to propagate through all the balls to lights 1 & 2, because if the player starts down the hall at a time just before the lights switch, then it will switch when the player crosses the left boundary of controller X... rather than when s/he is closer or already in the room.  Granted, this not a major issue, but why half-ass something when you don't have to? :D

This setup (and anything remotely like it, including examples 2 & 3) works because whenever a ball  receives a command from another ball to turn its lights on, it first checks its timer information against the current time.  If its lights shouldn't be enabled because the current time is not between the begin and endtime set, then it ignores the enable command and disables its lights instead.  Even though each Ball A in examples 2, 3, and 4 actually only sends enableNoWait() commands on its own, the attached balls still know what to do with their lights when the time comes.

Here's the actual code that handles this (important parts bolded, and modified slightly to be more readable):

FUNCTION enableNoWait(bool notUsed = false)
Parent.enable()
allLightsOn = enableLights()

IF (allLightsOn == False)
disableLights()
allLightsOff = True
ENDIF

IF ((OnTimerAutoUpdate > 0) && (OnTimer))
registerForUpdate(OnTimerAutoUpdate)
ENDIF
ENDFUNCTION

The only time enableLights() could ever return False is when the ball is on a timer AND the current time is not between the begin and endtime (or specialCaseNoEnables is set to True, of course).

Pretty slick, right?  Yeah, I thought you'd think so.  ;)  The usability of this entire feature hinges on those few lines.

Well, I think that pretty much does it.  If there's any questions, I'm sure you'll let me know.

Go back to main tutorial page

Friday, February 17, 2012

SKYRIM CK - Scripting Tutorial - Dynamic Lighting (With Video)

JUST IN TIME LIGHTING


Example Lighting Layout (1 - 8 are shadow casting lights, and A - C are scripting triggers to control the lighting)























Skyrim looks good, but it can always look better.  One thing we can do is add some more dynamic lighting to scenes to increase realism.  However, by doing this, you will quickly learn about one the engine's biggest limitations:

You can't have more than 4 shadow casting lights visible at a time (usually).  Otherwise, the engine will choose which lights to kill based on viewing angle, distance to player, etc.

"Ok", you might say, "I can live with that."  Actually, you can't.  Or, not if you want to add significantly more dynamic lights.  You'll just end up with flickering lights everywhere -- which introduces the engine's next biggest flaw:

The engine is particularly unclever about when and which lights to drop, when left to its own devices.


Thankfully, the CK is powerful enough to allow us to create our own tools to help us out.  Items labeled A, B, and C examples of these tools which I created.  LightingBallControllers (in yellow), and their accompanying LightingBalls (in blue) allow me (and you) to directly control the lighting in the area.

How they work:  Simply put, the LightingBallControllers talk to their LightingBalls (again, these are in blue), and the LightingBalls talk to the lights.  Each LightingBall  has a set of references for lights in the area (set in the object's script properties window).  As a player passes through one of the LightingBallControllers, they interact with their attached LightingBalls (up to 4) to find which set of lights to enable and disable.  It does this by answering the question: "Which ball is the player closest to?"  When it gets its answer, it tells that lightingball to enable the lights it controls.  Also, it tells each of its other lightingballs to disable their lights.

A controller can have anywhere from 2 - 4 balls, and each ball can control from 0 - 4 lights.

Advanced:  Also, each ball can be separately configured to ignore disable and/or enable commands from its controller.  To have 0 lights on a ball, both specialCases flags have to be set on the ball.  Also, two or more controllers can talk to the same lightingball.

It's powerful because it's so simple, and because they work with any object.  So, there's no need for custom scripts on the lights!  You just set up your lights normally, then drop down your controllers and balls.  Once you show the controller where their balls are and show the balls where the lights are, they'll do their magic.  I call this process rigging.

Advanced:  Nothing's stopping you from rigging a ball to any ObjectReference, so it doesn't actually have to be a light.


An example how the LightingBalls could be rigged to five
dynamic shadow casting lights
To create a LightingBallController or LightingBall, click on the "Create Trigger" button in the CK, and add the respective script to them.  Set name, size, shape, and color to taste.


To rig a controller to their balls: 


a) Use the properties button on controller's Script tabs, then click on ball_1
b) Set the reference to a ball
c) Repeat for ball_2, etc.


The same process is used to rig balls to their lights.


Once you create a set, you can just copy and paste them around.


This example assumes that I've already rigged the balls to their controllers.  As you can see, I've rigged the lights to the balls in such a way that they break line of sight.  Obviously, this is a simplistic example because you have to consider their light radius... but, you get the point!  Assuming everything is properly placed, lights will turn on just in time, and the player will never notice.

I'll run through two scenarios detailing what will happen when:

1) Player is at light #1, and is running towards light #5.  Lights 1 - 4 are already on.  Light 5 is off:

As player exits the top side of Controller A, light 3 turns off and light 5 turns on.
As the player exits the right side of Controller B, light 2 turns off.
As the player exits the right side of Controller C, lights 1 and 4 turn off.

2) Player is at light #5, and is running towards light #1.  Light 5 is on, and 1 - 4 are off.:

As the player exits the left side of Controller C, lights 1 and 4 turn on.
As the player exits the left side of Controller B, light 2 turns on.
As player exits the bottom side of Controller A, light 3 turns on and light 5 turns off.  


Why aren't there any lights rigged to the other ball on both Controllers B & C?  To show that you don't have to.  Or, maybe you're just freeing them up to let the engine handle the rest.  Or, maybe you didn't get to it yet.

Or, maybe I didn't want to clutter up the example with stuff on the other side of the map. ;)

From the CK, showing actual controllers with their balls, along with lights 4 & 5 (pink X's)



















Advanced:  If you actually want the CK to show lines connecting your balls to lights, you can.  You just have to set up a linked reference between the balls and lights.  It won't actually do anything besides drawing a line, however.

Here's a video demonstration of my scripts in action for the above example (the one showing 5 lights in close proximity).  The character is shown starting from light 5, and going back around again.


There's actually 7 dynamic lights visible in this video (2 are way down the hall).  I run up to 5 of them (those shown in the above example), so you can see their shadows.

Without my scripts running, that area would look broken, as shown in this video:


My scripts are still running, but I disabled the my balls' ability to disable lights via the specialCaseNoDisables flag.  So, they still enable them... just nothing ever gets disabled.  I did it this way, because all my lights are disabled by default.  But I assure you, this is how the game engine normally handles lighting (as anyone who has tried to do this can attest to).

If you compare it to the example diagram above, you'll see that the engine is "flickering" between lights 3 and 5.  Even though light #5 is obviously not visible through the wall, and there are 4 other lights that are closer, the game engine doesn't seem to care.  According to its own internal alien logic, light #5 is the best choice at that distance and viewing angle.  I didn't show it in the video, but if I had then run up to light #3, it would've also "flickered".

The source code is available below, and on the Nexus.  The newest version is currently v1.12.





Scripts ( v1.12 source and compiled, along with an example .esp):
Nexus

Scripts (just the source for 1.12):
LightingBall.psc
LightingBallController.psc