Difference between revisions of "Extending ColladaConv"

From Horde3D Wiki
Jump to: navigation, search
m
(Beginnings of the tutorial - a lot of the convertion code added)
Line 1: Line 1:
Author: [[User:Stuckie|Stuckie]]
+
{| border="0"
 +
| {{ContentBlock|width=800|color=white
 +
|content={{!!}}
 +
== 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.
+
'''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.
 
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!
 
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 programmer - 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! ==
 +
 +
{{CppSourceCode|
 +
description= Adding Lights to converter.cpp|
 +
code=
 +
<source lang="cpp" line="1">
 +
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();
 +
}
 +
</source>
 +
}}
 +
 +
== Cameras For All ==
 +
 +
{{CppSourceCode|
 +
description= Adding Cameras to converter.cpp|
 +
code=
 +
<source lang="cpp" line="1">
 +
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();
 +
 +
}
 +
</source>
 +
}}
 +
 +
== Scripted Physics and Objects ==
 +
 +
{{CppSourceCode|
 +
description= Adding Entity Info to converter.cpp|
 +
code=
 +
<source lang="cpp" line="1">
 +
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();
 +
}
 +
</source>
 +
}}
 +
 +
== Building The Level Loader ==
 +
 +
{{CppSourceCode|
 +
description= Adding Level Loader Info to converter.cpp|
 +
code=
 +
<source lang="cpp" line="1">
 +
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();
 +
}
 +
</source>
 +
}}
 +
 +
}}
 +
|  valign="top" | {{Extension_Summary|
 +
name = Extending ColladaConv|
 +
screenshot = H3DPlaceHolder.png|
 +
description = Example on Extending ColladaConv|
 +
version = 0.0|
 +
horde3dversion = 1.0 beta2|
 +
released = W.I.P|
 +
author = [[User:Stuckie|Steven "Stuckie" Campbell]]|
 +
}}
 +
|}

Revision as of 23:53, 2 October 2008

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 programmer - 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();
}
Extending ColladaConv
H3DPlaceHolder.png
Example on Extending ColladaConv
Version: 0.0
Compatible with Horde3D: 1.0 beta2
Release date: W.I.P
Author(s): Steven "Stuckie" Campbell