Creating custom materials in C
by Gerald Fishel · in Torque Game Engine Advanced · 12/13/2008 (9:37 pm) · 5 replies
Hi all,
I kinda asked this before, but it was added to another post and might have gone unseen due to that.
I have a need to construct custom materials in my C++ code.
As an example, I'll have a scene file that I am loading that contains a number of objects, and each object has a texture map and a lightmap. Different objects will sometimes use the same texture, but the lightmaps will be unique to each object.
So, basically I'd want to create a material along the lines of this:
Theoretically I could probably just export a .cs file with all of the definitions, and I'll probably add that as an optional functionality for tweaking and debugging things, but it would be highly convenient for me to be able to do it all in my C++ code as well.
Any pointers to documentation or examples on how to do this would be greatly appreciated.
Thanks,
Gerald
I kinda asked this before, but it was added to another post and might have gone unseen due to that.
I have a need to construct custom materials in my C++ code.
As an example, I'll have a scene file that I am loading that contains a number of objects, and each object has a texture map and a lightmap. Different objects will sometimes use the same texture, but the lightmaps will be unique to each object.
So, basically I'd want to create a material along the lines of this:
new CustomMaterial(floor1mat)
{
shader = KripLightmap;
texture[0] = "~/data/shapes/test/floortile";
texture[1] = "~/data/shapes/test/lm_floor1";
};Theoretically I could probably just export a .cs file with all of the definitions, and I'll probably add that as an optional functionality for tweaking and debugging things, but it would be highly convenient for me to be able to do it all in my C++ code as well.
Any pointers to documentation or examples on how to do this would be greatly appreciated.
Thanks,
Gerald
#2
Thanks, that did the trick. For closure, the following code will construct the example material I posted:
What is the significance of doing registerObject on the material? I haven't dug into the code much, but it seems that I can use the material without calling registerObject. The only thing I noticed differently is that when I do call it, I have to use the "~/data/etc..." for the file path otherwise it wants to prepend "common/serverScripts/". If I don't use registerObject I can use an absolute path, or one that is relative to the working directory.
Best Regards,
Gerald
12/13/2008 (11:29 pm)
Hi Phillip,Thanks, that did the trick. For closure, the following code will construct the example material I posted:
CustomMaterial *myMaterial = new CustomMaterial();
if (myMaterial->registerObject(StringTable->insert("floor1mat")))
{
myMaterial->mShaderData = static_cast<ShaderData*>(Sim::findObject( "KripLightmap" ) );
myMaterial->texFilename[0] = "~/data/shapes/test/floortile.png";
myMaterial->texFilename[1] = "~/data/shapes/test/lm_floor1.png";
} What is the significance of doing registerObject on the material? I haven't dug into the code much, but it seems that I can use the material without calling registerObject. The only thing I noticed differently is that when I do call it, I have to use the "~/data/etc..." for the file path otherwise it wants to prepend "common/serverScripts/". If I don't use registerObject I can use an absolute path, or one that is relative to the working directory.
Best Regards,
Gerald
#3
In Material::onAdd it's setting the material's base path to the path of the current script file, thinking that it's a material script file, but the current script file at that time is "common/serverScripts/missionDownload.cs".
So if I set myMaterial->mPath as the path to where my textures are I can just specify the name of the texture without the path, or I can set mPath to "" and specify an absolute path or a path relative to the game directory.
12/13/2008 (11:54 pm)
Okay, I see what's happening with the file path. In Material::onAdd it's setting the material's base path to the path of the current script file, thinking that it's a material script file, but the current script file at that time is "common/serverScripts/missionDownload.cs".
So if I set myMaterial->mPath as the path to where my textures are I can just specify the name of the texture without the path, or I can set mPath to "" and specify an absolute path or a path relative to the game directory.
#4
Note that calling registerObject() will indirectly cause the object's onAdd() method to be called (in C++ and in some cases script as well). The general assumption in all onAdd() methods is that the initial data has already been set. Particularly, all script fields set in the script new operator are set before an object is registered.
Another thing to keep in mind is that in general constant strings should use the string table. This is particularly true of filenames. Note that this is a gross generalization and is not always the case, but is the case as far as all script accessible strings go. Note also that this does not apply to engines that have the String class, which right now AFAIK is just TGEA 1.8. In either case, registerObject() adds the object name to the string table itself, so you don't have to when passing it in.
So, with the above in mind, assuming TGEA 1.7.x and ignoring your (correct) observations about paths, the following is equivalent to the original script code:
Note also that if registerObject() fails (and it can and will fail), it does not delete the object. You have to do that yourself.
For the sake of completeness: There are a couple of fringe cases where it's valid to not register the object. However, you don't want to even consider them without a decent understanding of how the sim and console work.
T.
Edit: Side note ... the problems caused by not registering an object are subtle and sometimes hard to track down. They will almost always eventually result in a crash, and you have to be really lucky for it to be anywhere near object creation. Fortunately, the signs of an unregistered object are fairly easy to spot in the debugger once you have fixed a couple of them.
12/14/2008 (12:39 am)
Every SimObject derived object must have registerObject() called on it otherwise it is not properly registered with the sim. Conversely, every object that has been registered with the sim must be deleted with deleteObject() and not the normal delete operator.Note that calling registerObject() will indirectly cause the object's onAdd() method to be called (in C++ and in some cases script as well). The general assumption in all onAdd() methods is that the initial data has already been set. Particularly, all script fields set in the script new operator are set before an object is registered.
Another thing to keep in mind is that in general constant strings should use the string table. This is particularly true of filenames. Note that this is a gross generalization and is not always the case, but is the case as far as all script accessible strings go. Note also that this does not apply to engines that have the String class, which right now AFAIK is just TGEA 1.8. In either case, registerObject() adds the object name to the string table itself, so you don't have to when passing it in.
So, with the above in mind, assuming TGEA 1.7.x and ignoring your (correct) observations about paths, the following is equivalent to the original script code:
CustomMaterial *myMaterial = new CustomMaterial();
myMaterial->mShaderData = static_cast<ShaderData*>(Sim::findObject( "KripLightmap" ) );
myMaterial->texFilename[0] = StringTable->insert("~/data/shapes/test/floortile.png");
myMaterial->texFilename[1] = StringTable->insert("~/data/shapes/test/lm_floor1.png");
if(! myMaterial->registerObject("floor1mat"))
{
delete myMaterial;
myMaterial = NULL;
}Note also that if registerObject() fails (and it can and will fail), it does not delete the object. You have to do that yourself.
For the sake of completeness: There are a couple of fringe cases where it's valid to not register the object. However, you don't want to even consider them without a decent understanding of how the sim and console work.
T.
Edit: Side note ... the problems caused by not registering an object are subtle and sometimes hard to track down. They will almost always eventually result in a crash, and you have to be really lucky for it to be anywhere near object creation. Fortunately, the signs of an unregistered object are fairly easy to spot in the debugger once you have fixed a couple of them.
#5
Thanks a bunch for the explanation. That's good and useful information. I think I got it all working cleanly now.
Best Regards,
Gerald
12/14/2008 (9:36 am)
Hi Tom,Thanks a bunch for the explanation. That's good and useful information. I think I got it all working cleanly now.
Best Regards,
Gerald
Associate Phillip O'Shea
Violent Tulip
// Create CustomMaterial *myMaterial = new CustomMaterial(); // Register if (myMaterial->registerObject(StringTableEntry->insert("myMaterialName"))) { // Apply fields }