Extending ColladaConv
Synopsis: This will be a tutorial on extending ColladaConv to take into account extra features that modellers such as Maya can add to the Collada specifications.
Obviously, the converter ignores these by default, so this tutorial will show you how to edit the converter to pick them up, and do what you like with them.
This will only cover the editing of the converter to understand the extra attributes, not Horde itself!
Disclaimer
This code literally came from Plight of The Weedunks, and has been tweaked just enough to allow it to be integrated into Horde 1.0 Beta 2.Please be aware that this is quite badly written as we only had ten weeks to create the entire game, as well as add bits of engine code, link in with Horde and everything else!
That said, the code does work for the purposes we required.. and it does serve as a basis to build your own implementations from and as it's something I had to figure out, I thought I should share that knowledge with other people - or at least start it so that others may build upon it!
Please feel free to edit this code, fix the bugs, make it your own, and adapt it in any way that you see fit.
Introduction
When creating Plight of The Weedunks, we wanted to create as much of the level as we could in Maya. We wanted to offload the burden of setting up all the scripted objects, lights and and physics attributes from us - the programmers - to the designer.
Luckily, Maya can export it's lights and cameras with ease - but of course they were not picked up by default when we were coding the game! Physics wise, FCollada does support handing out physics data, but we needed a custom solution to fit our needs... so our technical artist coded up a quick little script which literally just added "Additional Attributes" in Maya in a nice little GUI, though we ended up just using it to set up the attribute section and right-clicking Maya's attribute box and adding our own in as needed.
This little tutorial will walk you through what I did to the converter in order to pick up the extra bits we had Maya add to the Collada file, and also show you how we then spat this information out into custom Lua scripts for the engine to pick up on. You may want to do something else with this information, but the methods of getting it back from the Collada Document is exactly the same.
Let There Be Light!
Adding Lights to converter.cpp |
void Converter::writeLights( ColladaDocument &doc, const string &name )
{
std::string folder;
folder = ( "levels/" + name + "/lights" );
_mkdir( "levels" );
_mkdir( ( "levels/" + name ).c_str() );
_mkdir( folder.c_str() );
ofstream outf;
outf.open( ( folder + "/" + name + ".lights.lua").c_str(), ios::out );
outf << " -- Plight of The Weedunks\n
-- (c) Team Caffiene 2008\n
-------------------------\n" << endl;
outf << "-- Light Information for " << name << ".\n";
outf << "function " << name << "_LIGHTS()" << endl;
for( unsigned int i = 0; i < _lights.size(); ++i )
{
string id = _lights[i]->daeInstance->url;
DaeLight *light = doc.libLights.findLight( id );
if (light)
{
Vec3f trans, rot, scale;
_lights[i]->matRel.decompose( trans, rot, scale );
rot.x = radToDeg( rot.x );
rot.y = radToDeg( rot.y );
rot.z = radToDeg( rot.z );
outf << "\t" << light->name
<< " = Horde:AddLightNode( 0, \"" << light->name
<< "\", LightMat, \"LIGHTING\", \"SHADOWMAP\" );"
<< endl;
outf << "\tHorde:SetNodeTransform( "
<< light->name
<< ", " << trans.x << ", " << trans.y << ", " << trans.z
<< ", " << rot.x << ", " << rot.y << ", " << rot.z
<< ", " << scale.x << ", " << scale.y << ", " << scale.z
<< " );" << endl;
if(light->point)
{
outf << "\tHorde:SetNodeParamFloat( "
<< light->name
<< ", \"FOV\", 360 );"
<< endl;
outf << "\tHorde:SetNodeParamFloat( "
<< light->name
<< ", \"Radius\", 100 );"
<< endl;
}
if(light->spot)
{
outf << "\tHorde:SetNodeParamFloat( "
<< light->name
<< ", \"FOV\", " << light->outer_cone << " );"
<< endl;
outf << "\tHorde:SetNodeParamFloat( "
<< light->name
<< ", \"Radius\", 400 );"
<< endl;
}
outf << "\tHorde:SetNodeParamInt( "
<< light->name
<< ", \"ShadowMapCount\", 3 );"
<< endl;
outf << "\tHorde:SetNodeParamFloat( "
<< light->name
<< ", \"ShadowSplitLambda\", 0.9 );"
<< endl;
outf << "\tHorde:SetNodeParamFloat( "
<< light->name
<< ", \"ShadowMapBias\", 0.001 );"
<< endl;
outf << "\tHorde:SetNodeParamFloat( "
<< light->name
<< ", \"Col_R\", " << light->r * light->intensity << " );"
<< endl;
outf << "\tHorde:SetNodeParamFloat( "
<< light->name
<< ", \"Col_G\", " << light->g * light->intensity << " );"
<< endl;
outf << "\tHorde:SetNodeParamFloat( "
<< light->name
<< ", \"Col_B\", " << light->b * light->intensity << " );"
<< endl;
outf << endl;
}
}
outf << "end" << endl;
outf.close();
}
|
Cameras For All
Adding Cameras to converter.cpp |
void Converter::writeCameras( ColladaDocument &doc,const string &name )
{
std::string folder;
folder = ( "levels/" + name + "/cameras" );
_mkdir( folder.c_str() );
ofstream outf;
outf.open( (folder + "/" + name + ".camera.lua").c_str(), ios::out );
outf << " -- Plight of The Weedunks\n
-- (c) Team Caffiene 2008\n
-------------------------\n" << endl;
outf << "-- Camera Information for " << name << ".\n";
outf << "function " << name << "_CAMERAS()" << endl;
for( unsigned int i = 0; i < _cameras.size(); ++i )
{
string id = _cameras[i]->daeInstance->url;
DaeCamera *camera = doc.libCameras.findCamera( id );
if (camera)
{
Vec3f trans, rot, scale;
_cameras[i]->matRel.decompose( trans, rot, scale );
rot.x = radToDeg( rot.x );
rot.y = radToDeg( rot.y );
rot.z = radToDeg( rot.z );
outf << "\t" << camera->name << " = Horde:AddCameraNode( 0, \""
<< camera->name << "\", PipeRes );"
<< endl;
outf << "\tHorde:SetupCameraView( " << camera->name << ", "
<< camera->yfov << ", "
<< camera->aspect << ", "
<< camera->znear << ", "
<< camera->zfar << ");"
<< endl;
outf << "\tHorde:SetNodeTransform( "
<< camera->name << ", "
<< trans.x << ", " << trans.y << ", " << trans.z << ", "
<< rot.x << ", " << rot.y << ", " << rot.z << ", "
<< scale.x << ", " << scale.y << ", " << scale.z << " );"
<< endl;
outf << endl;
}
}
outf << "end" << endl;
outf.close();
}
|
Scripted Physics and Objects
Adding Entity Info to converter.cpp |
void Converter::writePhysics(ColladaDocument &doc, const string &name )
{
std::string folder;
folder = ( "levels/" + name + "/physics/static" );
_mkdir( ("levels/" + name + "/physics").c_str() );
_mkdir( folder.c_str() );
ofstream outf, outg, outh;
outf.open( ( folder + "/" + name + ".physics.static.lua" ).c_str(), ios::out );
outg.open( "StaticMovingMaterials.txt", ios::out );
outh.open( "DynamicMaterials.txt", ios::out );
outf << " -- Plight of The Weedunks\n
-- (c) Team Caffiene 2008\n
-------------------------\n" << endl;
outf << "-- Physics Information for " << name << ".\n";
outf << "function " << name << "_STATIC_PHYSICS()" << endl;
for( unsigned int i = 0; i < _meshes.size(); ++i )
{
Vec3f trans, rot, scale;
_meshes[i]->matRel.decompose( trans, rot, scale );
rot.x = -radToDeg( rot.x );
rot.y = -radToDeg( rot.y );
rot.z = -radToDeg( rot.z );
if (strcmp( _meshes[i]->daeNode->physicsObjectType.c_str(), "static" ) == 0)
{
outg << _meshes[i]->daeNode->physicsMaterial << endl;
outf << "\t" << _meshes[i]->daeNode->name << "_static = ODE:CreateStaticBox( "
<< scale.x << ", " << scale.y << ", " << scale.z << ", "
<< trans.x << ", " << trans.y << ", " << trans.z << ", "
<< rot.x << ", " << rot.y << ", " << rot.z << ", \""
<< _meshes[i]->daeNode->physicsMaterial << "\" );"
<< endl;
outf << endl;
}
}
outf << "end" << endl;
outf << endl;
outf.close();
folder = ( "levels/" + name + "/physics/dynamic" );
_mkdir( folder.c_str() );
outf.open( ( folder + "/" + name + ".physics.dynamic.lua").c_str(), ios::out );
outf << " -- Plight of The Weedunks\n
-- (c) Team Caffiene 2008\n
-------------------------\n" << endl;
outf << "-- Physics Information for " << name << ".\n";
outf << "function " << name << "_DYNAMIC_PHYSICS()" << endl;
for( unsigned int i = 0; i < _meshes.size(); ++i )
{
Vec3f trans, rot, scale;
_meshes[i]->matRel.decompose( trans, rot, scale );
rot.x = -radToDeg( rot.x );
rot.y = -radToDeg( rot.y );
rot.z = -radToDeg( rot.z );
if (strcmp( _meshes[i]->daeNode->physicsObjectType.c_str(), "dynamic" ) == 0)
{
outh << _meshes[i]->daeNode->physicsMaterial << endl;
outf << "\t" << _meshes[i]->daeNode->name << "_dynamic = ODE:CreateDynamicBox( "
<< scale.x << ", " << scale.y << ", " << scale.z << ", "
<< trans.x << ", " << trans.y << ", " << trans.z << ", "
<< rot.x << ", " << rot.y << ", " << rot.z << ", 10, \""
<< _meshes[i]->daeNode->physicsMaterial << "\" );"
<< endl;
outf << "\tHorde:FindNodes( LevelMod, \"" << _meshes[i]->daeNode->name << "\", \"Mesh\" );"
<< endl;
outf << "\tnodeHandle = Horde:GetNodeFindResult(0);" << endl;
outf << "\t" << name << "_DYNAMIC = {
next = " << name << "_DYNAMIC,
node = nodeHandle,
physics = " << _meshes[i]->daeNode->name << "_dynamic
};" << endl;
outf << endl;
}
}
outf << "end" << endl;
outf << endl;
outf.close();
folder = ( "levels/" + name + "/physics/scripted" );
_mkdir( folder.c_str() );
for( unsigned int i = 0; i < _meshes.size(); ++i )
{
if (!_meshes[i]->daeNode->physicsMaterial.empty())
{
Vec3f trans, rot, scale;
_meshes[i]->matRel.decompose( trans, rot, scale );
rot.x = -radToDeg( rot.x );
rot.y = -radToDeg( rot.y );
rot.z = -radToDeg( rot.z );
if ((strcmp( _meshes[i]->daeNode->physicsObjectType.c_str(), "moving" ) == 0)
|| (strcmp( _meshes[i]->daeNode->physicsObjectType.c_str(), "scripted" ) == 0))
{
outf.open( ( folder + "/" + _meshes[i]->daeNode->name + ".physics.lua").c_str(), ios::out );
outg << _meshes[i]->daeNode->physicsMaterial << endl;
outf << " -- Plight of The Weedunks\n
-- (c) Team Caffiene 2008\n
-------------------------\n" << endl;
outf << "-- Physics Information for " << _meshes[i]->daeNode->name << ".\n";
outf << "function " << _meshes[i]->daeNode->name << "_OBJECT ()" << endl;
outf << "\t" << _meshes[i]->daeNode->name
<< "_scripted = ODE:CreateMovingBox( "
<< scale.x << ", " << scale.y << ", " << scale.z << ", "
<< trans.x << ", " << trans.y << ", " << trans.z << ", "
<< rot.x << ", " << rot.y << ", " << rot.z << ", \""
<< _meshes[i]->daeNode->physicsMaterial << "\" );"
<< endl;
outf << "\tHorde:FindNodes( LevelMod, \""
<< _meshes[i]->daeNode->name << "\", \"Mesh\" );"
<< endl;
outf << "\tnodeHandle = Horde:GetNodeFindResult(0);" << endl;
outf << "\t" << name << "_SCRIPTED = {
next = " << name << "_SCRIPTED,
node = nodeHandle,
physics = " << _meshes[i]->daeNode->name << "_scripted
};" << endl;
outf << "\t" << _meshes[i]->daeNode->name << "_posX = " << trans.x << ";" << endl;
outf << "\t" << _meshes[i]->daeNode->name << "_posY = " << trans.y << ";" << endl;
outf << "\t" << _meshes[i]->daeNode->name << "_posZ = " << trans.z << ";" << endl;
outf << "\t" << _meshes[i]->daeNode->name << "_rotX = " << rot.x << ";" << endl;
outf << "\t" << _meshes[i]->daeNode->name << "_rotY = " << rot.y << ";" << endl;
outf << "\t" << _meshes[i]->daeNode->name << "_rotZ = " << rot.z << ";" << endl;
outf << "end" << endl;
outf << endl;
outf.close();
}
}
}
outg.close();
outh.close();
}
|
Building The Level Loader
Adding Level Loader Info to converter.cpp |
void Converter::writeLevel( ColladaDocument &doc,const string &name )
{
std::string folder;
folder = ( "levels/" + name );
ofstream outf;
outf.open( ( folder + "/" + name + ".load.lua" ).c_str(), ios::out );
outf << " -- Plight of The Weedunks\n
-- (c) Team Caffiene 2008\n
-------------------------\n" << endl;
outf << "-- Load Level Data" << endl;
outf << "dofile (\"data/scripts/" << folder << "/" << name << ".spawners.lua\");" << endl;
outf << "dofile (\"data/scripts/" << folder << "/" << name << ".targets.lua\");" << endl;
outf << "dofile (\"data/scripts/" << folder << "/" << name << ".waypoints.lua\");" << endl;
outf << "dofile (\"data/scripts/" << folder << "/cameras/" << name << ".camera.lua\");" << endl;
outf << "dofile (\"data/scripts/" << folder << "/lights/" << name << ".lights.lua\");" << endl;
outf << "dofile (\"data/scripts/" << folder << "/physics/static/" << name << ".physics.static.lua\");" << endl;
outf << "dofile (\"data/scripts/" << folder << "/physics/dynamic/" << name << ".physics.dynamic.lua\");" << endl;
for( unsigned int i = 0; i < _meshes.size(); ++i )
if (!_meshes[i]->daeNode->physicsMaterial.empty())
if ((strcmp( _meshes[i]->daeNode->physicsObjectType.c_str(), "moving" ) == 0)
|| (strcmp( _meshes[i]->daeNode->physicsObjectType.c_str(), "scripted" ) == 0))
outf << "dofile (\"data/scripts/"
<< folder
<< "/physics/scripted/"
<< _meshes[i]->daeNode->name
<< ".physics.lua\");"
<< endl;
outf << "\n-- Call Function\n" << endl;
outf << "function " << name << "_LOAD()" << endl;
outf << "\t" << name << "_CAMERAS();" << endl;
outf << "\t" << name << "_LIGHTS();" << endl;
outf << "\t" << name << "_STATIC_PHYSICS();" << endl;
outf << "\t" << name << "_DYNAMIC_PHYSICS();" << endl;
outf << "\t" << "setWaypoints();" << endl;
outf << "\t" << "createSpawners();" << endl;
outf << "\t" << "setTargetables();" << endl;
for( unsigned int i = 0; i < _meshes.size(); ++i )
if (!_meshes[i]->daeNode->physicsMaterial.empty())
if ((strcmp( _meshes[i]->daeNode->physicsObjectType.c_str(), "moving" ) == 0)
|| (strcmp( _meshes[i]->daeNode->physicsObjectType.c_str(), "scripted" ) == 0))
outf << "\t" << _meshes[i]->daeNode->name << "_OBJECT();" << endl;
outf << "end" << endl;
outf.close();
outf.open( ( folder + "/" + name + ".update.lua" ).c_str(), ios::out );
outf << " -- Plight of The Weedunks\n
-- (c) Team Caffiene 2008\n
-------------------------\n" << endl;
outf << name << "_SCRIPTED = nil;" << endl;
outf << name << "_DYNAMIC = nil;\n" << endl;
outf << "function " << name << "_UPDATE()\n" << endl;
outf << "\tlocal list = " << name << "_DYNAMIC;" << endl;
outf << "\twhile list do" << endl;
outf << "\t\tHorde:SetNodeTransformODEDynamicNoScale( list.node, list.physics );" << endl;
outf << "\t\tlist = list.next;" << endl;
outf << "\tend" << endl;
outf << "\tlocal list = " << name << "_SCRIPTED;" << endl;
outf << "\twhile list do" << endl;
outf << "\t\tHorde:SetNodeTransformODEScriptedNoScale( list.node, list.physics );" << endl;
outf << "\t\tlist = list.next;" << endl;
outf << "\tend" << endl;
outf << "end" << endl;
outf.close();
outf.open( ( folder + "/" + name + ".targets.lua" ).c_str(), ios::out );
outf << " -- Plight of The Weedunks\n
-- (c) Team Caffiene 2008\n
-------------------------\n" << endl;
outf << "function setTargetables()" << endl;
for( unsigned int i = 0; i < _meshes.size(); ++i )
if (!_meshes[i]->daeNode->targetable.empty())
{
Vec3f trans, rot, scale;
_meshes[i]->matRel.decompose( trans, rot, scale );
rot.x = -radToDeg( rot.x );
rot.y = -radToDeg( rot.y );
rot.z = -radToDeg( rot.z );
outf << "\tTARGETS = {
next = TARGETS,
x = " << trans.x << ", y = " << trans.y << ", z = " << trans.z << ",
name = \"" << _meshes[i]->daeNode->name << "\"
};" << endl;
}
outf << "end" << endl;
outf.close();
outf.open( ( folder + "/" + name + ".waypoints.lua" ).c_str(), ios::out );
outf << " -- Plight of The Weedunks\n
-- (c) Team Caffiene 2008\n
-------------------------\n" << endl;
outf << "function setWaypoints()" << endl;
for( unsigned int i = 0; i < _meshes.size(); ++i )
if (!_meshes[i]->daeNode->waypointType.empty())
{
Vec3f trans, rot, scale;
_meshes[i]->matRel.decompose( trans, rot, scale );
rot.x = -radToDeg( rot.x );
rot.y = -radToDeg( rot.y );
rot.z = -radToDeg( rot.z );
outf << "\tAI:AddWayPoint( " << _meshes[i]->daeNode->waypointType << ", \""
<< _meshes[i]->daeNode->name << "\", "
<< trans.x << ", " << trans.y << ", " << trans.z << " ); "
<< endl;
}
outf << "end" << endl;
outf.close();
outf.open( ( folder + "/" + name + ".spawners.lua" ).c_str(), ios::out );
outf << " -- Plight of The Weedunks\n
-- (c) Team Caffiene 2008\n
-------------------------\n" << endl;
outf << "function createSpawners()" << endl;
for( unsigned int i = 0; i < _meshes.size(); ++i )
if (!_meshes[i]->daeNode->SpawnDelay.empty())
{
Vec3f trans, rot, scale;
_meshes[i]->matRel.decompose( trans, rot, scale );
rot.x = -radToDeg( rot.x );
rot.y = -radToDeg( rot.y );
rot.z = -radToDeg( rot.z );
outf << "\tSPAWNERS = {
next = SPAWNERS,
x = " << trans.x << ", y = " << trans.y + scale.y/2 + 5 << ", z = " << trans.z << ",
numofbert = " << _meshes[i]->daeNode->NumberofBert << ",
numofernie = " << _meshes[i]->daeNode->NumberofErnie << ",
timer = 0,
bert = 0,
ernie = 0,
delay = " << _meshes[i]->daeNode->SpawnDelay << ",
name = \"" << _meshes[i]->daeNode->name << "\"
};" << endl;
}
outf << "end" << endl;
outf.close();
}
|
|