Interiors on the Fly
by Quentin Headen · in Torque Game Engine · 11/06/2007 (2:48 pm) · 20 replies
Hello everyone. I just wanted to know, how can I add an interior object on the fly using code. If you know the answer, PLEEASE let me know! :)
Thanks
Thanks
About the author
Just your average programmer who tries to finish the projects he starts. :) I am currently focused on creating games with Torque engines. My website is http://phaseshiftsoftware.com
#2
The torque mission files are simply script files. If you want to know how to create an interior. Start a new level, place your interior and save it. Then open your mission file and copy the block that creates the object you want!
Lets have an example (taken from the Stronghold mission):
11/11/2007 (3:22 am)
While technically correct, Lee's response lacks usefulness for anyone that is not familiar with Torque.The torque mission files are simply script files. If you want to know how to create an interior. Start a new level, place your interior and save it. Then open your mission file and copy the block that creates the object you want!
Lets have an example (taken from the Stronghold mission):
new InteriorInstance() {
canSaveDynamicFields = "1";
position = "376.075 333.165 227.003";
rotation = "0 0 -1 19.4806";
scale = "1 1 1";
interiorFile = "~/data/interiors/cottage/cottage.dif";
useGLLighting = "0";
showTerrainInside = "0";
};
#3
11/11/2007 (11:56 am)
Could you do something like the following once you have the code you want.function addObject(%file, %x, %y, %z, %pan, %tilt, %roll, %scale)
{
new InteriorInstance() {
canSaveDynamicFields = "1";
position = %x, %y, %z;
rotation = %pan, %tilt, %roll;
scale = %scale, %scale, %scale;
interiorFile = %file;
useGLLighting = "0";
showTerraininside = "0";
};
}
#4
@Johnathon: Basically yes, but due to some Torquescript realities you gotta kinda escape out a lot of the stuff. It occurs to me that my own simple Vehicle Spawner resource has a good example of this. Check out the first function on the page, function mySpawn.
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=12892
11/11/2007 (1:24 pm)
@Brian: See, you made another assumption--how do you know Quentin isn't already familiar with Torque. My response would send him delving down the right path of inquiry, which is more useful an answer than simply serving up code.@Johnathon: Basically yes, but due to some Torquescript realities you gotta kinda escape out a lot of the stuff. It occurs to me that my own simple Vehicle Spawner resource has a good example of this. Check out the first function on the page, function mySpawn.
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=12892
#5
11/11/2007 (4:10 pm)
@Lee: It's okay, you continue to try to get more information. I'll continue actually helping people.
#6
11/11/2007 (4:14 pm)
@Brian: wtf ever. It's okay, you continue issuing random responses and snarky comments to other posters. I'll continue trying to get to the root of issues, and helping people to help themselves.
#7
also,
this isn't the way rotation parameters are set. it uses 4 fields. the first 3 are a vector which specifies the alignment of the object, the last parameter is an angle in radians which specifies the rotation around the alignment vector.
11/11/2007 (4:23 pm)
Jonathan that function would work fine for a single player game. for a multiplayer game, it would only work properly if the function was called from the server.also,
rotation = %pan, %tilt, %roll;
this isn't the way rotation parameters are set. it uses 4 fields. the first 3 are a vector which specifies the alignment of the object, the last parameter is an angle in radians which specifies the rotation around the alignment vector.
#8
maybe a onAdd call so it reflushes the lighting to reset it.. but could cause some laggish issues when you do that..
TomFeni
11/11/2007 (4:35 pm)
You would also need a relight function or your interiors would be black..maybe a onAdd call so it reflushes the lighting to reset it.. but could cause some laggish issues when you do that..
TomFeni
#9
I have developed a method for single-interior relighting. It only takes ~200ms in RELEASE compile for a single, 2 story 5 room interior on a 2Ghz Pentium-m processor. So there might be an ever so slight pause when placing a new building, but who cares? I am also confdent I can lower that number with some optimizing.
The overall goal is to implement a day/night relighting that takes place incrementally over an extended period of time. A hit of 200ms every 5 seconds for 1 minute wouldn't be excessive - especially if I can optimize it down even more.
The only thing preventing me from releasing a resource for this special method of relighting, is the ability to test it more. The SunObject in TGE won't let me edit it via script of console. Rather, setting the elevation variable has no effect until I go into the mission editor and click on the object pane. There is something simple I am missing, but a week of investigating the problem hasn't yielded any results. Any help in solving this would be appreciated, then I could release my findings for everyone! Good luck with things in the meantime.
11/12/2007 (8:35 pm)
Quote:You would also need a relight function or your interiors would be black..
I have developed a method for single-interior relighting. It only takes ~200ms in RELEASE compile for a single, 2 story 5 room interior on a 2Ghz Pentium-m processor. So there might be an ever so slight pause when placing a new building, but who cares? I am also confdent I can lower that number with some optimizing.
The overall goal is to implement a day/night relighting that takes place incrementally over an extended period of time. A hit of 200ms every 5 seconds for 1 minute wouldn't be excessive - especially if I can optimize it down even more.
The only thing preventing me from releasing a resource for this special method of relighting, is the ability to test it more. The SunObject in TGE won't let me edit it via script of console. Rather, setting the elevation variable has no effect until I go into the mission editor and click on the object pane. There is something simple I am missing, but a week of investigating the problem hasn't yielded any results. Any help in solving this would be appreciated, then I could release my findings for everyone! Good luck with things in the meantime.
#10
There are two routes to go here:
Create a new method that is exposed to script on the sun to set the elevation/azimuth and make a call to setMaskBits(UpdateMask); This is probably the most proper way to make it work.
Another route:
The object's ::inspectPostApply field probably needs to be called to replicate what the editor does. The editor code calls this directly from C++ when you edit fields. You could expose this method to script. Actually, in the codebase I'm looking at there's an "apply" method exposed to script which does this now. That may exist for you also.
11/13/2007 (9:39 am)
Jason,There are two routes to go here:
Create a new method that is exposed to script on the sun to set the elevation/azimuth and make a call to setMaskBits(UpdateMask); This is probably the most proper way to make it work.
Another route:
The object's ::inspectPostApply field probably needs to be called to replicate what the editor does. The editor code calls this directly from C++ when you edit fields. You could expose this method to script. Actually, in the codebase I'm looking at there's an "apply" method exposed to script which does this now. That may exist for you also.
#11
we also would v. much like to have non-global illumination.
200ms every 5 seconds for a minute sounds a little annoying to me, to be honest.
you could also consider pre-calculating all the lighting files and distributing them w/ the installer.
11/13/2007 (11:45 am)
Quote:I have developed a method for single-interior relighting.i would love to hear an outline of that.
we also would v. much like to have non-global illumination.
200ms every 5 seconds for a minute sounds a little annoying to me, to be honest.
you could also consider pre-calculating all the lighting files and distributing them w/ the installer.
#12
Another avenue I might explore is compiler optimization. I don't know if VS2005 express is necessarily the best compiler for speed - or if that even matters for Torque. Funny enough, with all the timers and processing time calculations being done, I have to wonder if that isn't accounting for some of the 200ms...
I also know Windows with VB.net has trouble resolving below ~37ms ticks unless you get ahold of the system microsecond timer. I used to use a custom timer library whenever I needed real time results. Until I delve into Torque's timers I won't know just how accurate they really are.
Lastly, a 2Ghz CPU is hardly "fast" by today's standards. Bumping it to a 3Ghz system (let alone dual core if multithreading helps) would drop that down to 133ms per interior. A 30fps refresh rate would be 1 frame every 33ms, so you'd lose about 4-8 frames during each update process. A problem for FPS perhaps.. maybe not so much for an RPG or RTS game? I guess we'll see!
And thank you Brian for the suggestions. I'll dig into things later and see which method might work best!
11/13/2007 (12:21 pm)
Pre-calculating won't work in a real-time dynamic environment, unfortunately. In any case this does fix the problem with placing single interiors dynamically and quickly relighting them upon placement. I'll admit that more work (optimization) needs to be done by me before I'll feel confident in doing relighting over the course of a day. I still think it can be done... but yeah, being able to move the sun was key :P Another avenue I might explore is compiler optimization. I don't know if VS2005 express is necessarily the best compiler for speed - or if that even matters for Torque. Funny enough, with all the timers and processing time calculations being done, I have to wonder if that isn't accounting for some of the 200ms...
I also know Windows with VB.net has trouble resolving below ~37ms ticks unless you get ahold of the system microsecond timer. I used to use a custom timer library whenever I needed real time results. Until I delve into Torque's timers I won't know just how accurate they really are.
Lastly, a 2Ghz CPU is hardly "fast" by today's standards. Bumping it to a 3Ghz system (let alone dual core if multithreading helps) would drop that down to 133ms per interior. A 30fps refresh rate would be 1 frame every 33ms, so you'd lose about 4-8 frames during each update process. A problem for FPS perhaps.. maybe not so much for an RPG or RTS game? I guess we'll see!
And thank you Brian for the suggestions. I'll dig into things later and see which method might work best!
#13
The problem is actually getting the sun object. I want to do :
Now there's a few ways I can think about getting the sun object, none of them are making sense to me at the moment.
1). Rewrite the function above so I can grab the sun object, then I can call its functions.
2). Pass the client Id through the console function, then somehow look up the server ID so I can reference the object by its ID value. Again, I've no idea how to do this. I suspect I need to :
Where mID is the server side object Id. I can grab the client side ID with TorqueScript but am not familiar with correlating that with a server side ID yet.
Do folks just stumble through the code for months before things start to make sense? I've read both 3D Game Programming books but they don't touch on editing the engine much. I find it amusing I was able to rewrite the lighting code, but can't get past the user/client/server object passing boundaries!
Thank you for any help you can give me, or for pointing me in the right direction to teach myself how to do what should be simple routines. :P
11/15/2007 (10:09 pm)
So I guess my messing with the engine code isn't getting my anywhere. I'm trying to expose the sun object's inspectPostApply method to the console. I've created a ConsoleFunction() that is called via a button for now (will automate is later), and it is being called correctly.The problem is actually getting the sun object. I want to do :
Sun *sunLight = GetMeTheSunObject???
if(sunLight != NULL)
{
sunLight->inspectPostApply();
}Now there's a few ways I can think about getting the sun object, none of them are making sense to me at the moment.
1). Rewrite the function above so I can grab the sun object, then I can call its functions.
2). Pass the client Id through the console function, then somehow look up the server ID so I can reference the object by its ID value. Again, I've no idea how to do this. I suspect I need to :
obj = Sim::findObject(mID);
obj->InspectPostApply();Where mID is the server side object Id. I can grab the client side ID with TorqueScript but am not familiar with correlating that with a server side ID yet.
Do folks just stumble through the code for months before things start to make sense? I've read both 3D Game Programming books but they don't touch on editing the engine much. I find it amusing I was able to rewrite the lighting code, but can't get past the user/client/server object passing boundaries!
Thank you for any help you can give me, or for pointing me in the right direction to teach myself how to do what should be simple routines. :P
#14
Try using a ConsoleMethod instead of a console function, then the obj is already resolved for you. (Search the code for ConsoleMethods for examples). Then in script you would just call Sun.myMethod();
As far as exploring the codebase goes, I normally look for things that already work similiar to what I need and track down how it works. By that point, I've hopefully learned enough to implement my changes, or find out what else I need to learn about. This is generic large code base advise, but it's worked for me on a lot of projects.
Hope that helps!
11/16/2007 (8:49 am)
Jason,Try using a ConsoleMethod instead of a console function, then the obj is already resolved for you. (Search the code for ConsoleMethods for examples). Then in script you would just call Sun.myMethod();
As far as exploring the codebase goes, I normally look for things that already work similiar to what I need and track down how it works. By that point, I've hopefully learned enough to implement my changes, or find out what else I need to learn about. This is generic large code base advise, but it's worked for me on a lot of projects.
Hope that helps!
#15
(in your .mis file)
And in your c++ code (wherever you need the pointer to the sun object)
11/16/2007 (9:47 am)
In the stock missions, the Sun is not created as a named object, but if you name it (with a unique name) in your .mis file, then you can do a Sim::FindObject within c++ to find a pointer to the object. Something along the lines of:(in your .mis file)
new Sun([b]SunObject[/b]) {
canSaveDynamicFields = "1";
azimuth = "45";
elevation = "45";
color = "0.35 0.35 0.35 1";
ambient = "0.15 0.15 0.27 1";
CastsShadows = "1";
rotation = "1 0 0 0";
direction = "0.57735 0.57735 -0.57735";
scale = "1 1 1";
position = "0 0 0";
};And in your c++ code (wherever you need the pointer to the sun object)
Sun* sunlight = dynamic_cast<Sun*>(Sim::findObject("SunObject"));
if (sunlight)
{
// do stuff
}
#16
11/17/2007 (9:43 am)
Really good stuff guys. Thanks so much for the help!
#17
Gui controls can be explored by going through the code in something like guibitmapbutton.
Dave
11/17/2007 (10:35 am)
Brian, that is extremely sound advice. Look for something that you know works in a similar way, or explore one of the large classes like shapebase or player, and you will see one of everything pretty much! Each of those usually has some kind of rendering function so you get to see how those work, and what they trace down to.Gui controls can be explored by going through the code in something like guibitmapbutton.
Dave
#18
I've narrowed it down to the
function that is taking all the time. Inside the function is a loop that runs once per surface. On a model with 3700 surfaces that takes about 200ms. So I thought I would be sly, and I rewrote the relighting code so it only ran as many surfaces as it could in 75ms, then it exited without deleting the SceneLighting object. The next time it ran it would be fed the number of surfaces it completed last time, and start from there. Over the course of several seconds the building would have all its surfaces recalculated, and then saved off.
Well, while this would seem work in theory, I must be missing something about the engine. During a normal relight the following gets called at the end :
If I remove those calls (so the object isn't deleted and is ready for me to continue calculations on next time) the render engine stops dead. So a relight gets called and works for 75ms, then exists and the render engine freezes. Then when the scheduler calls the relight again it processes another 75ms worth of surfaces, and exits. Eventually the object completes and the render engine unpauses.
Could someone maybe help me out with this? I'm trying to devise a method to just process a few surfaces at a time in order to distribute the processing. I'm so darn close I can taste it :P
I'm on AOL IM a lot too, so please feel free to catch me there at Juason1. Thanks!
11/22/2007 (6:37 pm)
So I've not been satisfied with my attempts at relighting single interiors. I can do it, but there is still a perceivable hiccup each time it occurs. What I thought was ~200ms per interior was really closer to 300ms once I improved the location of the stopwatches. Then I tweaked like mad and got it back down to ~200ms.. but its still too much.I've narrowed it down to the
void SceneLighting::InteriorProxy::light(LightInfo *light)
function that is taking all the time. Inside the function is a loop that runs once per surface. On a model with 3700 surfaces that takes about 200ms. So I thought I would be sly, and I rewrote the relighting code so it only ran as many surfaces as it could in 75ms, then it exited without deleting the SceneLighting object. The next time it ran it would be fed the number of surfaces it completed last time, and start from there. Over the course of several seconds the building would have all its surfaces recalculated, and then saved off.
Well, while this would seem work in theory, I must be missing something about the engine. During a normal relight the following gets called at the end :
lighting->completed(true); lighting->deleteObject();
If I remove those calls (so the object isn't deleted and is ready for me to continue calculations on next time) the render engine stops dead. So a relight gets called and works for 75ms, then exists and the render engine freezes. Then when the scheduler calls the relight again it processes another 75ms worth of surfaces, and exits. Eventually the object completes and the render engine unpauses.
Could someone maybe help me out with this? I'm trying to devise a method to just process a few surfaces at a time in order to distribute the processing. I'm so darn close I can taste it :P
I'm on AOL IM a lot too, so please feel free to catch me there at Juason1. Thanks!
#19
We'll see what happens.
11/22/2007 (10:24 pm)
On a whim I tried to create a copy of the SceneLighting class, but that seems to throw an exception during the SceneLighting deconstructor. I guess I'm not understanding the significance of keeping this data around...ConsoleFunction(RelightSingleObject, S32, 2, 2, "RelightSingleObject()")
{
SceneLighting templighting;
SceneLighting * lighting;
// process the relighting here, this is where *lighting gets registered and almost all the normal setup for the relighting takes place.
NumSurfaces = lighting->light(RunCount); // runs the surfaces starting from RunCount
// runs for a max of 75ms and returns how far it got
// returns 9999 if it completed all the surfaces
if (NumSurfaces == 9999) // the object was completed,
// now we can finish it up and save it back to the lightmap file
{
lighting->completed(true);
lighting->deleteObject();
return(0);
}
else // otherwise we did NOT get to all the surfaces. So we save off the class data and will
// return here on the next running of this function.
{
templighting = *lighting; // this is what causes an exception to be thrown following
// the SceneLighting deconstructor.
lighting->completed(true);
lighting->deleteObject();
return(NumSurfaces);
}We'll see what happens.
#20
At the end of a relight this function gets called :
I found that if I leave this destroySelectiveBitmaps part in, relighting only half of the faces results in half the model turning dark every relight. If I remove it, the entire model stays textured but the old shadows never get removed, and the entire model increases in brightness.
My guess is the downloadGLTextures is loading up 12 generated textures for my particular model, and the destroy Bitmaps is destroying 18 old shadow bitmaps. Maybe, I've tried fiddling with which bitmaps get destroyed to no avail.
The idea I had, was ok lets process as many surfaces as we can in 70ms. Those surfaces somehow (I still haven't located how or where) get turned into textures. Those textures then get merged with the shadow bitmaps and turned into a final texture, then are mapped onto the object. If I can prevent the old textures from being blacked out, then the new ones will just map over the old ones and in two passes most .DIF models will be updated.
One big unknown, is why deletebitmaps is blacking out part of my model if it only has to do with shadow information.
Again, the sun isn't moving very fast. So if it takes 5-10 seconds to pass over the whole DIF you shouldn't notice. I'm going to keep trying new things with the deletebitmaps function. If I can only delete the shadow bitmaps for those textures that have been regenerated, perhaps the old shadows won't persist.
This might seem like rambling, but I'm hoping to save someone else many days of trial and error in this same endeavor to get selective/real time lightmap generation going. It is quite the challenge. As always, advice is welcome ;)
11/23/2007 (11:04 am)
So I am heading down a slightly different path now, but I'm still not understanding quite a few aspects of the relighting engine.At the end of a relight this function gets called :
void SceneLighting::completed(bool success, bool killbitmaps)
{
// process the cached lighting files
processCache();
if(success)
{
AssertFatal(smUseVertexLighting == Interior::smUseVertexLighting, "SceneLighting::completed: vertex lighting state changed during scene light");
// cannot do anything if vertex state has changed (since we only load in what is needed)
if(smUseVertexLighting == Interior::smUseVertexLighting)
{
if(!smUseVertexLighting)
{
gInteriorLMManager.downloadGLTextures();
gInteriorLMManager.destroyBitmaps();
}
else
gInteriorLMManager.destroyTextures();
}
}
if(gCompleteCallback && gCompleteCallback[0])
Con::executef(1, gCompleteCallback);
}I found that if I leave this destroySelectiveBitmaps part in, relighting only half of the faces results in half the model turning dark every relight. If I remove it, the entire model stays textured but the old shadows never get removed, and the entire model increases in brightness.
My guess is the downloadGLTextures is loading up 12 generated textures for my particular model, and the destroy Bitmaps is destroying 18 old shadow bitmaps. Maybe, I've tried fiddling with which bitmaps get destroyed to no avail.
The idea I had, was ok lets process as many surfaces as we can in 70ms. Those surfaces somehow (I still haven't located how or where) get turned into textures. Those textures then get merged with the shadow bitmaps and turned into a final texture, then are mapped onto the object. If I can prevent the old textures from being blacked out, then the new ones will just map over the old ones and in two passes most .DIF models will be updated.
One big unknown, is why deletebitmaps is blacking out part of my model if it only has to do with shadow information.
Again, the sun isn't moving very fast. So if it takes 5-10 seconds to pass over the whole DIF you shouldn't notice. I'm going to keep trying new things with the deletebitmaps function. If I can only delete the shadow bitmaps for those textures that have been regenerated, perhaps the old shadows won't persist.
This might seem like rambling, but I'm hoping to save someone else many days of trial and error in this same endeavor to get selective/real time lightmap generation going. It is quite the challenge. As always, advice is welcome ;)
Torque Owner Lee Latham
Default Studio Name