diff --git a/AUTHORS b/AUTHORS index 017c35b6b..57a130d16 100644 --- a/AUTHORS +++ b/AUTHORS @@ -64,7 +64,7 @@ Ulrich Hertlein - support for texture subloading in osg::Texture - osg::Sequence support. - improvements to the pfb loader. - - .lwo and .obj plugins. + - .lwo, .obj and .x (DirectX model) plugins. Axel Volley - support for OBJECT_LINEAR and EYE_LINEAR TexGen modes. diff --git a/Make/makedirdefs b/Make/makedirdefs index 462da8847..8394b82f4 100644 --- a/Make/makedirdefs +++ b/Make/makedirdefs @@ -29,6 +29,7 @@ PLUGIN_DIRS = \ rgb\ ac3d\ lib3ds\ + directx\ flt\ geo\ iv\ diff --git a/NEWS b/NEWS index ef8871241..6c3e8f44e 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,5 @@ + OSG News (most significant items from ChangeLog) ================================================ @@ -29,12 +30,16 @@ OSG News (most significant items from ChangeLog) Support added for reading and writing of callbacks in the .osg format. + Support added for reading and writing of osg::Shapes in the .osg format. + Support added for reading OpenFlight comment records into osg::Node description fields. Improvements to the GEO loaders. - Support for encoded text types in osgText. + From Tree, Support for encoded text types in osgText. + + From Ulrich Hertlein, new DirectX .x model loader. 13th November 2002 - OpenSceneGraph-0.9.2.tar.gz diff --git a/VisualStudio/VisualStudio.dsw b/VisualStudio/VisualStudio.dsw index ecffad1ab..cb95f4253 100644 --- a/VisualStudio/VisualStudio.dsw +++ b/VisualStudio/VisualStudio.dsw @@ -1398,6 +1398,24 @@ Package=<4> ############################################################################### +Project: "osgPlugin directx"=.\osgPlugins\directx\directx.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name Core osg + End Project Dependency + Begin Project Dependency + Project_Dep_Name Core osgDB + End Project Dependency +}}} + +############################################################################### + Project: "osgPlugin osg"=.\osgPlugins\osg\dot_osg.dsp - Package Owner=<4> Package=<5> diff --git a/VisualStudio/osgPlugins/directx/directx.dsp b/VisualStudio/osgPlugins/directx/directx.dsp new file mode 100644 index 000000000..b36d74b3b --- /dev/null +++ b/VisualStudio/osgPlugins/directx/directx.dsp @@ -0,0 +1,117 @@ +# Microsoft Developer Studio Project File - Name="osgPlugin directx" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=osgPlugin directx - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "directx.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "directx.mak" CFG="osgPlugin directx - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "osgPlugin directx - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "osgPlugin directx - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "osgPlugin directx - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../../lib" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GR /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /pdb:none /machine:I386 /nodefaultlib:"LIBC" /out:"../../../bin/osgdb_x.dll" /libpath:"../../../lib" +# SUBTRACT LINK32 /nodefaultlib + +!ELSEIF "$(CFG)" == "osgPlugin directx - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../../lib" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /vmg /vd0 /GR /GX /Zi /Od /I "../../../include" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WIN32" /D "_DEBUG" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /nodefaultlib:"LIBC" /out:"../../../bin/osgdb_xd.dll" /pdbtype:sept /libpath:"../../../lib" +# SUBTRACT LINK32 /nodefaultlib + +!ENDIF + +# Begin Target + +# Name "osgPlugin directx - Win32 Release" +# Name "osgPlugin directx - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\..\src\osgPlugins\directx\directx.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\osgPlugins\directx\ReaderWriterDirectX.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\..\Src\osgPlugins\directx\directx.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/src/osgPlugins/directx/Makefile b/src/osgPlugins/directx/Makefile new file mode 100644 index 000000000..ec6b88e9b --- /dev/null +++ b/src/osgPlugins/directx/Makefile @@ -0,0 +1,18 @@ +# +# $Id$ +# + +TOPDIR = ../../.. +include $(TOPDIR)/Make/makedefs + +CXXFILES =\ + ReaderWriterDirectX.cpp\ + directx.cpp + +LIBS += $(OSG_LIBS) $(OTHER_LIBS) + +TARGET_BASENAME = x +include $(TOPDIR)/Make/cygwin_plugin_def +PLUGIN = $(PLUGIN_PREFIX)$(TARGET_BASENAME).$(PLUGIN_EXT) + +include $(TOPDIR)/Make/makerules diff --git a/src/osgPlugins/directx/ReaderWriterDirectX.cpp b/src/osgPlugins/directx/ReaderWriterDirectX.cpp new file mode 100644 index 000000000..34a8cc4d4 --- /dev/null +++ b/src/osgPlugins/directx/ReaderWriterDirectX.cpp @@ -0,0 +1,295 @@ +// -*-c++-*- + +/* + * $Id$ + * + * DirectX file converter for OpenSceneGraph. + * Copyright (c)2002 Ulrich Hertlein + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "directx.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +/** + * OpenSceneGraph plugin wrapper/converter. + */ +class ReaderWriterDirectX : public osgDB::ReaderWriter +{ +public: + ReaderWriterDirectX() { } + + virtual const char* className() { + return "DirectX Reader/Writer"; + } + + virtual bool acceptsExtension(const std::string& extension) { + return osgDB::equalCaseInsensitive(extension,"x") ? true : false; + } + + virtual ReadResult readNode(const std::string& fileName, + const osgDB::ReaderWriter::Options* options); + +private: + osg::Geode* convertFromDX(DX::Object& obj, bool flipTexture); +}; + +// Register with Registry to instantiate the above reader/writer. +osgDB::RegisterReaderWriterProxy g_readerWriter_DirectX_Proxy; + + +// Read node +osgDB::ReaderWriter::ReadResult ReaderWriterDirectX::readNode(const std::string& fileName, + const osgDB::ReaderWriter::Options* options) +{ + std::string ext = osgDB::getLowerCaseFileExtension(fileName); + if (!acceptsExtension(ext)) + return ReadResult::FILE_NOT_HANDLED; + + osg::notify(osg::INFO) << "ReaderWriterDirectX::readNode(" << fileName.c_str() << ")\n"; + + // Load DirectX mesh + DX::Object obj; + if (obj.load(fileName.c_str())) { + // Options? + bool flipTexture = true; + if (options) { + const std::string option = options->getOptionString(); + cerr << option << endl; + if (option.find("flipTexture") != string::npos) + flipTexture = false; + } + + // Convert to osg::Geode + osg::Geode* geode = convertFromDX(obj, flipTexture); + if (!geode) + return ReadResult::FILE_NOT_HANDLED; + + return geode; + } + + return ReadResult::FILE_NOT_HANDLED; +} + +// Convert DirectX mesh to osg::Geode +osg::Geode* ReaderWriterDirectX::convertFromDX(DX::Object& obj, bool flipTexture) +{ + // Fetch mesh + const DX::Mesh* mesh = obj.getMesh(); + if (!mesh) + return NULL; + + const DX::MeshMaterialList* meshMaterial = obj.getMeshMaterialList(); + if (!meshMaterial) + return NULL; + + const DX::MeshNormals* meshNormals = obj.getMeshNormals(); + if (!meshNormals) { + obj.generateNormals(); + meshNormals = obj.getMeshNormals(); + } + if (!meshNormals) + return NULL; + + const DX::MeshTextureCoords* meshTexCoords = obj.getMeshTextureCoords(); + if (!meshTexCoords) + return NULL; + + /* + * - MeshMaterialList contains a list of Material and a per-face + * information with Material is to be applied to which face. + * - Mesh contains a list of Vertices and a per-face information + * which vertices (three or four) belong to this face. + * - MeshNormals contains a list of Normals and a per-face information + * which normal is used by which vertex. + * - MeshTextureCoords contains a list of per-vertex texture coordinates. + * + * - Uses left-hand CS with Y-up, Z-into + * obj_x -> osg_x + * obj_y -> osg_z + * obj_z -> osg_y + * + * - Polys are CW oriented + */ + vector geomList; + + unsigned int i; + for (i = 0; i < meshMaterial->material.size(); i++) { + + const DX::Material& mtl = meshMaterial->material[i]; + osg::StateSet* state = new osg::StateSet; + + // Material + osg::Material* material = new osg::Material; + state->setAttributeAndModes(material); + + float alpha = mtl.faceColor.alpha; + osg::Vec4 ambient(mtl.faceColor.red, + mtl.faceColor.green, + mtl.faceColor.blue, + alpha); + material->setAmbient(osg::Material::FRONT, ambient); + material->setDiffuse(osg::Material::FRONT, ambient); + + material->setShininess(osg::Material::FRONT, mtl.power); + + osg::Vec4 specular(mtl.specularColor.red, + mtl.specularColor.green, + mtl.specularColor.blue, alpha); + material->setSpecular(osg::Material::FRONT, specular); + + osg::Vec4 emissive(mtl.emissiveColor.red, + mtl.emissiveColor.green, + mtl.emissiveColor.blue, alpha); + material->setEmission(osg::Material::FRONT, emissive); + + // Transparency? Set render hint & blending + if (alpha < 1.0f) { + state->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + state->setMode(GL_BLEND, osg::StateAttribute::ON); + } + else + state->setMode(GL_BLEND, osg::StateAttribute::OFF); + + unsigned int textureCount = mtl.texture.size(); + for (unsigned int j = 0; j < textureCount; j++) { + // Load image + osg::Image* image = osgDB::readImageFile(mtl.texture[j]); + if (!image) + continue; + + // Texture + osg::Texture2D* texture = new osg::Texture2D; + texture->setImage(image); + texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT); + texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::REPEAT); + state->setTextureAttributeAndModes(j, texture); + } + + // Geometry + osg::Geometry* geom = new osg::Geometry; + geomList.push_back(geom); + + geom->setStateSet(state); + + // Arrays to hold vertices, normals, and texcoords. + geom->setVertexArray(new osg::Vec3Array); + geom->setNormalArray(new osg::Vec3Array); + geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); + if (textureCount) { + // All texture units share the same array + osg::Vec2Array* texCoords = new osg::Vec2Array; + for (unsigned int j = 0; j < textureCount; j++) + geom->setTexCoordArray(j, texCoords); + } + + geom->addPrimitiveSet(new osg::DrawArrayLengths(osg::PrimitiveSet::POLYGON)); + } + + assert(mesh->faces.size() == meshMaterial->faceIndices.size()); + + // Add faces to Geometry + for (i = 0; i < meshMaterial->faceIndices.size(); i++) { + + // Geometry for Material + unsigned int mi = meshMaterial->faceIndices[i]; + osg::Geometry* geom = geomList[mi]; + + // #pts of this face + unsigned int np = mesh->faces[i].size(); + ((osg::DrawArrayLengths*) geom->getPrimitiveSet(0))->push_back(np); + + assert(np == meshNormals->faceNormals[i].size()); + + osg::Vec3Array* vertexArray = (osg::Vec3Array*) geom->getVertexArray(); + osg::Vec3Array* normalArray = (osg::Vec3Array*) geom->getNormalArray(); + osg::Vec2Array* texCoordArray = (osg::Vec2Array*) geom->getTexCoordArray(0); + + // Add vertices, normals, texcoords + for (unsigned int j = 0; j < np; j++) { + + // Convert CW to CCW order + unsigned int jj = (j > 0 ? np - j : j); + + // Vertices + unsigned int vi = mesh->faces[i][jj]; + if (vertexArray) { + // Transform Xleft/Yup/Zinto to Xleft/Yinto/Zup + const DX::Vector& v = mesh->vertices[vi]; + vertexArray->push_back(osg::Vec3(v.x,v.z,v.y)); + } + + // Normals + unsigned int ni = meshNormals->faceNormals[i][jj]; + if (normalArray) { + // Transform Xleft/Yup/Zinto to Xleft/Yinto/Zup + const DX::Vector& n = meshNormals->normals[ni]; + normalArray->push_back(osg::Vec3(n.x,n.z,n.y)); + } + + // TexCoords + if (texCoordArray) { + const DX::Coords2d& tc = (*meshTexCoords)[vi]; + osg::Vec2 uv; + if (flipTexture) + uv.set(tc.u, 1.0f - tc.v); // Image is upside down + else + uv.set(tc.u, tc.v); + texCoordArray->push_back(uv); + } + } + } + + // Add non-empty nodes to Geode + osg::Geode* geode = new osg::Geode; + for (i = 0; i < geomList.size(); i++) { + osg::Geometry* geom = geomList[i]; + if (((osg::Vec3Array*) geom->getVertexArray())->size()) + geode->addDrawable(geom); + } + + // Back-face culling + osg::StateSet* state = new osg::StateSet; + geode->setStateSet(state); + + osg::CullFace* cullFace = new osg::CullFace; + cullFace->setMode(osg::CullFace::BACK); + state->setAttributeAndModes(cullFace); + + /* + * TODO: + * Smooth normals if we previously did a 'generateNormals'? + * (Would create a dependency on osgUtil.) + */ + + return geode; +} diff --git a/src/osgPlugins/directx/directx.cpp b/src/osgPlugins/directx/directx.cpp new file mode 100644 index 000000000..dd1e7051c --- /dev/null +++ b/src/osgPlugins/directx/directx.cpp @@ -0,0 +1,633 @@ +// -*-c++-*- + +/* + * $Id$ + * + * Loader for DirectX .x files. + * Copyright (c)2002 Ulrich Hertlein + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "directx.h" + +#include +#include + +#include +#include + + +using namespace DX; +using namespace std; + + +// Tokenize a string +void tokenize(const string& str, vector& tokens, + const string& delimiters = " \t\r\n;,") +{ + string::size_type lastPos = str.find_first_not_of(delimiters, 0); + string::size_type pos = str.find_first_of(delimiters, lastPos); + + while (string::npos != pos || string::npos != lastPos) { + tokens.push_back(str.substr(lastPos, pos - lastPos)); + lastPos = str.find_first_not_of(delimiters, pos); + pos = str.find_first_of(delimiters, lastPos); + } +} + + +// Constructor +Object::Object(const char* filename) +{ + _textureCoords = NULL; + _materialList = NULL; + _normals = NULL; + _mesh = NULL; + + load(filename); +} + + +// Destructor +Object::~Object() +{ + clear(); +} + + +// Clear object +void Object::clear() +{ + if (_textureCoords) { + delete _textureCoords; + _textureCoords = NULL; + } + + if (_materialList) { + delete _materialList; + _materialList = NULL; + } + + if (_normals) { + delete _normals; + _normals = NULL; + } + + if (_mesh) { + delete _mesh; + _mesh = NULL; + } +} + + +// Load +bool Object::load(const char* filename) +{ + if (!filename) + return false; + + // Delete previous + clear(); + + // Open + ifstream fin(filename); + if (fin.bad()) { + cerr << "Object::load: Unable to open: " << filename << endl; + return false; + } + + // Parse + parseSection(fin); + fin.close(); + + return true; +} + + +/* + * Generate per-face normals + */ +bool Object::generateNormals() +{ + if (!_mesh) + return false; + + // Forget old normals + if (_normals) { + delete _normals; + _normals = NULL; + } + + cerr << "*** GenerateNormals\n"; + + // Create normals + _normals = new MeshNormals; + + // Loop over MeshFaces + unsigned int i, fi; + for (fi = 0; fi < _mesh->faces.size(); fi++) { + + // Collect polygon vertices + vector poly; + unsigned int n = _mesh->faces[fi].size(); + + if (n < 3) + continue; + for (i = 0; i < n; i++) { + unsigned int idx = _mesh->faces[fi][i]; + poly.push_back(_mesh->vertices[idx]); + } + + // Edge vectors + Vector e0; + e0.x = poly[1].x - poly[0].x; + e0.y = poly[1].y - poly[0].y; + e0.z = poly[1].z - poly[0].z; + + Vector e1; + e1.x = poly[2].x - poly[0].x; + e1.y = poly[2].y - poly[0].y; + e1.z = poly[2].z - poly[0].z; + + // Cross-product of e0,e1 + Vector normal; + normal.x = e0.y * e1.z - e0.z * e1.y; + normal.y = e0.z * e1.x - e0.x * e1.z; + normal.z = e0.x * e1.y - e0.y * e1.x; + + // Normalize + float len = sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z); + normal.x /= len; + normal.y /= len; + normal.z /= len; + + // Add normal index + unsigned int ni = _normals->normals.size(); + _normals->normals.push_back(normal); + + // All vertices of this face share this vertex + MeshFace mf; + mf.resize(n); + for (i = 0; i < n; i++) + mf[i] = ni; + _normals->faceNormals.push_back(mf); + } + + return true; +} + + +/********************************************************************** + * + * Private + * + **********************************************************************/ + +// Read 'TextureFilename' +void Object::readTexFilename(ifstream& fin, TextureFilename& texture) +{ + char buf[256]; + vector token; + + cerr << "*** TexFilename\n"; + while (fin.getline(buf, sizeof(buf))) { + + // Tokenize + token.clear(); + tokenize(buf, token); + if (token.size() == 0) + continue; + if (token[0] == "}") + break; + + // Strip " if present + string line = buf; + size_t pos = line.find('"'); + if (pos != string::npos) { + size_t end = line.rfind('"'); + int len = (end != string::npos ? (end-pos-1) : (line.size()-pos)); + texture = line.substr(pos+1, len); + } + else + texture = token[0]; + //cerr << "tex=" << texture << endl; + } +} + + +// Parse 'Material' +void Object::parseMaterial(ifstream& fin, Material& material) +{ + char buf[256]; + vector token; + + unsigned int i = 0; + + //cerr << "*** Material\n"; + while (fin.getline(buf, sizeof(buf))) { + + // Tokenize + token.clear(); + tokenize(buf, token); + if (token.size() == 0) + continue; + if (token[0] == "}") + break; + + if (token[0] == "TextureFilename") { + TextureFilename tf; + readTexFilename(fin, tf); + + material.texture.push_back(tf); + //cerr << "num tex=" << material.texture.size() << endl; + } + else + switch (i) { + case 0: { + // ColorRGBA + material.faceColor.red = atof(token[0].c_str()); + material.faceColor.green = atof(token[1].c_str()); + material.faceColor.blue = atof(token[2].c_str()); + material.faceColor.alpha = atof(token[3].c_str()); + i++; + } break; + case 1: { + // Power + material.power = atof(token[0].c_str()); + i++; + } break; + case 2: { + // ColorRGB + material.specularColor.red = atof(token[0].c_str()); + material.specularColor.green = atof(token[1].c_str()); + material.specularColor.blue = atof(token[2].c_str()); + i++; + } break; + case 3: { + // ColorRGB + material.emissiveColor.red = atof(token[0].c_str()); + material.emissiveColor.green = atof(token[1].c_str()); + material.emissiveColor.blue = atof(token[2].c_str()); + i++; + } break; + } + } +} + + +// Read 'Coords2d' +void Object::readCoords2d(ifstream& fin, vector& v, unsigned int count) +{ + char buf[256]; + vector token; + + unsigned int i = 0; + + cerr << "*** Coords2d\n"; + while (i < count && fin.getline(buf, sizeof(buf))) { + + // Tokenize + token.clear(); + tokenize(buf, token); + if (token.size() == 0) + continue; + + Coords2d c; + c.u = atof(token[0].c_str()); + c.v = atof(token[1].c_str()); + v.push_back(c); + i++; + } +} + + +// Read 'MeshTextureCoords' +void Object::readMeshTexCoords(ifstream& fin) +{ + char buf[256]; + vector token; + + unsigned int nTextureCoords = 0; + + cerr << "*** MeshTextureCoords\n"; + while (fin.getline(buf, sizeof(buf))) { + + // Tokenize + token.clear(); + tokenize(buf, token); + if (token.size() == 0) + continue; + + if (strrchr(buf, '}') != 0) + break; + + // Create MeshTextureCoords + if (!_textureCoords) + _textureCoords = new MeshTextureCoords; + + // Texture coords + nTextureCoords = atoi(token[0].c_str()); + readCoords2d(fin, *_textureCoords, nTextureCoords); + + cerr << "nTextureCoords=" << nTextureCoords << "/" << _textureCoords->size() << endl; + + assert(nTextureCoords == _textureCoords->size()); + } +} + + +// Read index list +void Object::readIndexList(ifstream& fin, vector& v, unsigned int count) +{ + char buf[256]; + vector token; + + unsigned int i = 0; + + cerr << "*** IndexList\n"; + while (i < count && fin.getline(buf, sizeof(buf))) { + + // Tokenize + token.clear(); + tokenize(buf, token); + if (token.size() == 0) + continue; + + unsigned int idx = atoi(token[0].c_str()); + v.push_back(idx); + i++; + } +} + + +// Parse 'MeshMaterialList' +void Object::parseMeshMaterialList(ifstream& fin) +{ + char buf[256]; + vector token; + + unsigned int nMaterials = 0, nFaceIndices = 0; + + cerr << "*** MeshMaterialList\n"; + while (fin.getline(buf, sizeof(buf))) { + + // Tokenize + token.clear(); + tokenize(buf, token); + if (token.size() == 0) + continue; + + if (strrchr(buf, '}') != 0) + break; + else if (strrchr(buf, '{') != 0) { + if (token[0] == "Material") { + Material mm; + parseMaterial(fin, mm); + + _materialList->material.push_back(mm); + //cerr << "num mat=" << _materialList->material.size() << endl; + } + else { + cerr << "!!! MeshMaterialList: Section " << token[0] << endl; + parseSection(fin); + } + } + else if (nMaterials == 0) { + // Create MeshMaterialList + if (!_materialList) + _materialList = new MeshMaterialList; + + // Materials + nMaterials = atoi(token[0].c_str()); + //cerr << "nMaterials=" << nMaterials << endl; + } + else if (nFaceIndices == 0) { + // Face indices + nFaceIndices = atoi(token[0].c_str()); + readIndexList(fin, _materialList->faceIndices, nFaceIndices); + + cerr << "nFaceIndices=" << nFaceIndices << "/" << _materialList->faceIndices.size() << endl; + + assert(nFaceIndices == _materialList->faceIndices.size()); + } + } + + assert(nMaterials == _materialList->material.size()); +} + + +// Read 'Vector' +void Object::readVector(ifstream& fin, vector& v, unsigned int count) +{ + char buf[256]; + vector token; + + unsigned int i = 0; + + cerr << "*** Vector\n"; + while (i < count && fin.getline(buf, sizeof(buf))) { + + // Tokenize + token.clear(); + tokenize(buf, token); + if (token.size() == 0) + continue; + + Vector vec; + vec.x = atof(token[0].c_str()); + vec.y = atof(token[1].c_str()); + vec.z = atof(token[2].c_str()); + v.push_back(vec); + i++; + } +} + + +// Read 'MeshFace' +void Object::readMeshFace(ifstream& fin, vector& v, unsigned int count) +{ + char buf[256]; + vector token; + + unsigned int i = 0; + + cerr << "*** MeshFace\n"; + while (i < count && fin.getline(buf, sizeof(buf))) { + + // Tokenize + token.clear(); + tokenize(buf, token); + if (token.size() == 0) + continue; + + MeshFace mf; + unsigned int n = atoi(token[0].c_str()); + + for (unsigned int j = 0; j < n; j++) { + unsigned int idx = atoi(token[j+1].c_str()); + mf.push_back(idx); + } + v.push_back(mf); + i++; + } +} + + +// Parse 'MeshNormals' +void Object::parseMeshNormals(ifstream& fin) +{ + char buf[256]; + vector token; + + unsigned int nNormals = 0, nFaceNormals = 0; + + cerr << "*** MeshNormals\n"; + while (fin.getline(buf, sizeof(buf))) { + + // Tokenize + token.clear(); + tokenize(buf, token); + if (token.size() == 0) + continue; + + if (strrchr(buf, '}') != 0) + break; + else if (nNormals == 0) { + // Create MeshNormals + if (!_normals) + _normals = new MeshNormals; + + // Normals + nNormals = atoi(token[0].c_str()); + readVector(fin, _normals->normals, nNormals); + + cerr << "nNormals=" << nNormals << "/" << _normals->normals.size() << endl; +#ifdef NORMALIZE_NORMALS + for (unsigned int i = 0; i < _normals->normals.size(); i++) { + DX::Vector vec = _normals->normals[i]; + + // Normalize + float len = sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z); + vec.x /= len; + vec.y /= len; + vec.z /= len; + + _normals->normals[i] = vec; + } +#endif + + assert(nNormals == _normals->normals.size()); + } + else if (nFaceNormals == 0) { + // Face normals + nFaceNormals = atoi(token[0].c_str()); + readMeshFace(fin, _normals->faceNormals, nFaceNormals); + + cerr << "nFaceNormals=" << nFaceNormals << "/" << _normals->faceNormals.size() << endl; + + assert(nFaceNormals == _normals->faceNormals.size()); + } + } +} + + +// Parse 'Mesh' +void Object::parseMesh(ifstream& fin) +{ + char buf[256]; + vector token; + + unsigned int nVertices = 0, nFaces = 0; + + cerr << "*** Mesh\n"; + while (fin.getline(buf, sizeof(buf))) { + + // Tokenize + token.clear(); + tokenize(buf, token); + if (token.size() == 0) + continue; + + if (strrchr(buf, '}') != 0) { + break; + } + else if (strrchr(buf, '{') != 0) { + if (token[0] == "MeshMaterialList") + parseMeshMaterialList(fin); + else if (token[0] == "MeshNormals") + parseMeshNormals(fin); + else if (token[0] == "MeshTextureCoords") + readMeshTexCoords(fin); + else { + cerr << "!!! Mesh: Section " << token[0] << endl; + parseSection(fin); + } + } + else if (nVertices == 0) { + // Create mesh + if (!_mesh) + _mesh = new Mesh; + + // Vertices + nVertices = atoi(token[0].c_str()); + readVector(fin, _mesh->vertices, nVertices); + + cerr << "nVertices=" << nVertices << "/" << _mesh->vertices.size() << endl; + + assert(nVertices == _mesh->vertices.size()); + } + else if (nFaces == 0) { + // Faces + nFaces = atoi(token[0].c_str()); + readMeshFace(fin, _mesh->faces, nFaces); + + cerr << "nFaces=" << nFaces << "/" << _mesh->faces.size() << endl; + + assert(nFaces == _mesh->faces.size()); + } + else + cerr << "!!! " << buf << endl; + } +} + + +// Parse section +void Object::parseSection(ifstream& fin) +{ + char buf[256]; + vector token; + + while (fin.getline(buf, sizeof(buf))) { + + if (strrchr(buf, '}') != 0) { + //cerr << "!!! End section\n"; + break; + } + else if (strrchr(buf, '{') != 0) { + // Tokenize + token.clear(); + tokenize(buf, token); + if (token.size() == 0) + continue; + + //cerr << "!!! Section " << token[0] << endl; + if (token[0] == "Mesh") + parseMesh(fin); + else + parseSection(fin); + } + } +} diff --git a/src/osgPlugins/directx/directx.h b/src/osgPlugins/directx/directx.h new file mode 100644 index 000000000..87497a2bb --- /dev/null +++ b/src/osgPlugins/directx/directx.h @@ -0,0 +1,195 @@ +// -*-c++-*- + +/* + * $Id$ + * + * Loader for DirectX .x files. + * Copyright (c)2002 Ulrich Hertlein + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _DIRECTX_H_ +#define _DIRECTX_H_ + +#include +#include +#include + +namespace DX { + + /* + * DirectX templates + * http://astronomy.swin.edu.au/~pbourke/geomformats/directx/ + */ + + // Vector + typedef struct { + float x,y,z; + } Vector; + + // Coords2d + typedef struct { + float u,v; + } Coords2d; + + // ColorRGBA + typedef struct { + float red,green,blue,alpha; + } ColorRGBA; + + // ColorRGB + typedef struct { + float red,green,blue; + } ColorRGB; + + // IndexedColor + typedef struct { + unsigned int index; + ColorRGBA indexColor; + } IndexedColor; + + // TextureFilename + typedef std::string TextureFilename; + + // Material (potentially with multiple textures) + typedef struct { + ColorRGBA faceColor; + float power; + ColorRGB specularColor; + ColorRGB emissiveColor; + std::vector texture; + } Material; + + // MeshFace + typedef std::vector MeshFace; + + // MeshTextureCoords + typedef std::vector MeshTextureCoords; + + // MeshNormals + typedef struct { + std::vector normals; + std::vector faceNormals; + } MeshNormals; + + // MeshVertexColors. + typedef std::vector MeshVertexColors; + + // MeshMaterialList + typedef struct { + std::vector faceIndices; + std::vector material; + } MeshMaterialList; + + // Mesh + typedef struct { + std::vector vertices; + std::vector faces; + } Mesh; + + /** + * DirectX object. + */ + class Object { + public: + /// Constructor; loads filename if non-NULL. + Object(const char* filename = NULL); + + /// Destructor. + virtual ~Object(); + + /// Load from file; discards old data. + bool load(const char* filename); + + /** + * Generate normals. + * This function generates face normals and binds them to + * every vertex of this face. + */ + bool generateNormals(); + + /// Get MeshTextureCoords. + inline const MeshTextureCoords* getMeshTextureCoords() const { + return _textureCoords; + } + + /// Get MeshMaterialList. + inline const MeshMaterialList* getMeshMaterialList() const { + return _materialList; + } + + /// Get MeshNormals. + inline const MeshNormals* getMeshNormals() const { + return _normals; + } + + /// Get Mesh. + inline const Mesh* getMesh() const { + return _mesh; + } + + private: + /// Texture coordinates (per-vertex). + MeshTextureCoords* _textureCoords; + + /// Material list (per-face). + MeshMaterialList* _materialList; + + /// Normals (per-face). + MeshNormals* _normals; + + /// Mesh. + Mesh* _mesh; + + /// Clear object. + void clear(); + + /// Read 'TextureFilename'. + void readTexFilename(std::ifstream& fin, TextureFilename& texture); + + /// Parse 'Material'. + void parseMaterial(std::ifstream& fin, Material& material); + + /// Read 'Coords2d'. + void readCoords2d(std::ifstream& fin, std::vector& v, unsigned int count); + + /// Read 'MeshTextureCoords'. + void readMeshTexCoords(std::ifstream& fin); + + /// Read index list. + void readIndexList(ifstream& fin, std::vector& v, unsigned int count); + + /// Parse 'MeshMaterialList'. + void parseMeshMaterialList(std::ifstream& fin); + + /// Read 'Vector'. + void readVector(ifstream& fin, std::vector& v, unsigned int count); + + /// Read 'MeshFace'. + void readMeshFace(ifstream& fin, std::vector& v, unsigned int count); + + /// Parse 'MeshNormals'. + void parseMeshNormals(std::ifstream& fin); + + /// Parse 'Mesh'. + void parseMesh(std::ifstream& fin); + + /// Parse section until '}'; recurse as needed. + void parseSection(std::ifstream& fin); + }; +} // namespace + +#endif