Game Development Community

Creating dynamic lights in C

by Gerald Fishel · in Torque Game Engine Advanced · 12/14/2008 (10:58 pm) · 8 replies

Hi all,

So I've got my new scene objects working fairly well now, with high quality baked light maps exported from 3D Studio Max, polysoup collision detection and such goodies.

I'm also exporting the lights from 3ds max, and would like to also add them to the scene in TGEA to provide dynamic lighting and shadows. It would be nice, at least for now, to use the dynamic lights that ship with TGEA.

So how would I go about adding a light to the scene graph at runtime in C++?

As an example I'd like to be able to add a fxLight similar to the following to the scene:

new fxLight(test1) 
{
    canSaveDynamicFields = "1";
    Enabled = "1";
    position = "719.907 818.19 51.6203";
    rotation = "1 0 0 0";
    scale = "1 1 1";
    dataBlock = "FlameLight";
    Enable = "1";
    IconSize = "1";
};

Is it just a matter of constructing the fxLight object, setting the properties, and calling registerObject, or will I need to do other things to add it to the scene so that the flare renders and the light affects shadows and the player?

Thanks,
Gerald

#1
12/15/2008 (12:42 am)
Actually, while trying to figure out how to do this I realized that registering lights for dynamic lighting is pretty simple and it makes more sense to encapsulate the lights within my scene objects rather than using fxLights and adding them to the scene graph separately, since the lights are really meant to be tightly bound to these objects.

It would still be nice to be able to add objects to the scene graph through code rather than script though. I tried stepping through the debugger to see what steps were done as the mission script was being processed, and I think I came kinda close since I could see the objects listed in the world inspector tree list, but not visible in the scene.

And then I ended up with an error dialog that said "bad", which made the dog in me tuck my tail between my legs and look away guiltily, but didn't give me any particular insight into what the problem was ;)
#2
12/15/2008 (2:39 am)
Objects add themselves to the scene graph when they're registered and are removed when they're deregistered. You don't have to jump through any hoops to do it, and in fact attempting to manipulate the scene graph manually is probably a Really Bad Idea(tm).

Yes, this is exactly the same issue as with materials. It will also be exactly the same should you ever have the need to do things like create GuiControls or any other game object.

It sounds like you're suffering from a "well it's easy in script, but I have to do it in C++ for this particular reason so it must be tons harder" frame of mind. It's true that there are a few extra lines of code you need in C++, but the result is exactly the same and it's no harder at all.

Another side note, if you're creating objects in C++ you need to be aware of when they are destroyed. If you haven't explicitly handled that then you need to look into it. To throw you a small bone, mission related things (such as lights) can be thrown into the MissionCleanup group. That will ensure they're deleted when the mission is unloaded. If you don't already know how to do that in C++, I suggest trying to figure it out from the engine code before asking as it will help some key concepts mesh for you.

T.
#3
12/15/2008 (6:26 am)
Hi Tom,

You know, my first thought was that it was probably as simple as the materials, then I tried it with fxLight and it didn't work; renderObject and registerLights were never being called. So I then just assumed that it had to be more complicated than what I was attempting and went on a mission!

Since you told me that I was right the first time I just tried it again, same result, but ran the debugger through onAdd and discovered why: GameBase::mDataBlock was NULL so GameBase::onAdd was returning false. I was trying to set the data block the same way I was setting it with the materials, which set fxLight::mDataBlock but not GameBase::mDataBlock, which are different (Material is derived from SimObject so didn't have that problem). So then I called fxLight::onNewDataBlock instead and it worked. I saw that method before and assumed it was supposed to be an event handler and ignored it. Happens when I burn the midnight oil :)

The good news is that although it was a tactical failure, my initial mission was still fairly informative, part of which was that I learned how to add objects to SimGroups. Thanks for the tip on MissionCleanup (couldn't find a group called MissionCleanup, but there's a ClientMissionCleanup which seems to work).

The following will create the fxLight I posted:

fxLight* light = new fxLight();
        fxLightData* data = dynamic_cast<fxLightData*>(Sim::findObject( "FlameLight" ));
        if (data == NULL)
        {
               delete light;
               light = NULL;
               return false;
        }
	light->onNewDataBlock( data );
	light->setPosition( Point3F( 719.907, 818.19, 51.6203 ) );
	if (!light->registerObject("test1"))
	{
		delete light;
		light = NULL;
                return false;
	}
	SimGroup *cleanupgroup = dynamic_cast<SimGroup *>( Sim::findObject( "ClientMissionCleanup") );
	if( cleanupgroup != NULL )
	{
		cleanupgroup->addObject( light );
	}

Thanks again Tom.

Best Regards,
Gerald
#4
12/15/2008 (9:28 am)
Re: Setting datablocks ... I'd forgotten about that, sorry. You should be using setDataBlock() rather than calling onNewDatablock() directly. setDataBlock() will ensure the correct thing happens depening on if it's the client or server.

Re: other things ... MissionCleanup only exists on the server, ClientMissionCleanup sounds like the client side equivalent.

It sounds like you're creating the light on the client instead of the server. There may be a valid reason you're doing that, but without more information it sounds kind of weird. It might be a good idea if you describe the actual goal you're trying to achieve and how you're going about it now. It may just be that creating an fxLight is the wrong way to be going, or it may be that you're not using them properly, or it may be the right solution and i'm just imagining things.

T.
#5
12/15/2008 (2:13 pm)
Hi Tom,

I'm still a little iffy on how things work in the engine apparently. I was creating the fxLight on both the client and the server. (I was doing it from the onAdd method of my custom objects).

Now I am testing for isServerObject first, and only adding it once on the server, and that seems to work fine. However, there still is no "MissionCleanup" group at that point. Is that group created later on?

I did a search of the engine code for "MissionCleanup" and didn't find anything. The only thing I found was a reference to ClientMissionCleanup in particleEmitter::onAdd.

----

At this point I was just trying to learn how to add objects to the scene correctly in C++. I'm no longer really interested in adding lights that way per se as I think handling the lights within my objects makes more sense for what I'm trying to accomplish. But I'll eventually want to add other objects that the player can interact with, and that will probably be best handled by adding them to the scene in the way that I was trying to add fxLight.

I'll give you a rundown of what I'm ultimately trying to accomplish.

---

I am working on a prototype for a game that includes a lot of self-contained indoor scenes. Think something along the lines of Fallout 3. I've never been particularly impressed with the "interior" objects that are part of TGEA. I was going to try and use them anyway for the initial prototype, but I wasted a few days figuring out that Constructor is outputting bad collision detection data and decided that my time is better used working on something more like what I'll want in the final product.

So I'm creating new portal-based interior scene objects that can be modeled in 3D Studio Max/Maya/XSI/etc, with high quality pre-calculated lightmaps and normal maps.

I've got that part working fairly well now, and I wanted to be able to have the lights in the scene affect dynamic objects, cast dynamic shadows, etc.

As I said, I'm no longer really interested in using fxLight or similar added to the scene. This is how I'm handling those lights, maybe you can tell me if there's a better way:

1) From the onAdd of my scene objects, only on the client, I'm creating a LightInfo for each light in the scene by calling gClientSceneGraph->getLightManager()->createLightInfo();

2) Implementing registerLights and adding the lights to the lightmanager via LightManager::registerGlobalLight

3) In the renderObject for my scene objects I'm rendering the flares as billboards, similar to how fxLight does it.

Eventually I'll probably do things differently, but for the prototype that should all be good enough, and it's simple.

---

I'll also want to add other objects to the scene. i.e. movable furniture, collectible items, NPCs, etc. So that's what I'm still interested in adding separate objects to the scene via code for. References to these objects will be included in the interior scene objects.

Is the onAdd method of my scene objects the "right" place to also add these additional objects to the scene, or should I be doing it somewhere else?


Thanks a bunch,
Gerald
#6
12/15/2008 (2:29 pm)
BTW, for clarity, my scene objects are being added to the scene via script the "normal" way rather than C++, it's just the accessory objects that I want to add from C++.
#7
12/15/2008 (8:09 pm)
Quote:
I'm still a little iffy on how things work in the engine apparently. I was creating the fxLight on both the client and the server. (I was doing it from the onAdd method of my custom objects).

fxLight is a fully networked game object that's intended to be added from the mission editor, so you create it on the server and it will ghost itself over to clients. Which is why it seemed overkill for adding lighting to a custom object :)

Quote:
Now I am testing for isServerObject first, and only adding it once on the server, and that seems to work fine. However, there still is no "MissionCleanup" group at that point. Is that group created later on?

I did a search of the engine code for "MissionCleanup" and didn't find anything. The only thing I found was a reference to ClientMissionCleanup in particleEmitter::onAdd.

MissionCleanup is created and managed entirely in script. It's created when the mission is loaded and destroyed along with everything in it when the mission ends. It should be available from a Sim::findObject(), though may not be depending on when you call it. If the object you're creating is managed by another one (e.g. you have a FooSceneObject that holds a pointer to the fxLight) then you should be deleting the fxLight from the FooSceneObject and adding the FooSceneObject to MissionCleanup (probably in script).

Quote:
I was going to try and use them anyway for the initial prototype, but I wasted a few days figuring out that Constructor is outputting bad collision detection data and decided that my time is better used working on something more like what I'll want in the final product.

Side note, but TGEA requires use of a different map2dif than TGE and you may have been using the wrong one. Been a while since I did anything with Constructor though so I forget how it handles TGEA.

Quote:
I've got that part working fairly well now, and I wanted to be able to have the lights in the scene affect dynamic objects, cast dynamic shadows, etc.

(snip)

That sounds like the right way to do it. I'm not terribly familiar with the lighting code though so if it's wrong, maybe someone else will point it out :)

Quote:
Is the onAdd method of my scene objects the "right" place to also add these additional objects to the scene, or should I be doing it somewhere else?

It should be the right place, yes.

T.
#8
12/15/2008 (8:54 pm)
Thanks for all of your input, Tom.

Yeah, MissionCleanup wasn't being found with Sim::findObject. However, when I added fxLight to ClientMissionCleanup, it resulted in both the client and server instances being deleted when the mission is unloaded.

But what you are saying here does make sense. I will handle the cleanup from my object, and make sure my object is in the proper group. Currently I am adding my objects to the scene via the mission scripts, so I'm assuming that it's being added to the proper group for mission cleanup already (onRemove and the destructor is being called when the mission is unloaded).

As far as Constructor goes, you may be right about that as well. Constructor has a number of different options for exporting to DIF and I tried them all with the same result, but I have not checked to see what version of map2dif it was using or if that makes a difference. The nature of the error really doesn't sound like a map2dif issue to me, but I don't know enough about it to be sure. In the end, I've never been a big fan of that type of modeling, requiring the use of all convex brushes and such. I understand many of the reasons why it's been so widely used, but I think we're at a point now where it's not all that necessary.

Anyway, thanks again for all of your help and insight. I think I have this all figured out now, and I can get on with the business of building the game. When I have time I'm going to put together a series of resources documenting how to do all of the things I've been doing, since all of the ones I've found are outdated and no longer work.