From Ulrich Hertlein, DirectX .x model loader plugin.

This commit is contained in:
Robert Osfield 2003-01-08 22:52:27 +00:00
parent f91e141e7a
commit ba2c3a187d
9 changed files with 1284 additions and 2 deletions

View File

@ -64,7 +64,7 @@ Ulrich Hertlein <u.hertlein@bit-side.com>
- 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 <volley@acm.org>
- support for OBJECT_LINEAR and EYE_LINEAR TexGen modes.

View File

@ -29,6 +29,7 @@ PLUGIN_DIRS = \
rgb\
ac3d\
lib3ds\
directx\
flt\
geo\
iv\

7
NEWS
View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,295 @@
// -*-c++-*-
/*
* $Id$
*
* DirectX file converter for OpenSceneGraph.
* Copyright (c)2002 Ulrich Hertlein <u.hertlein@sandbox.de>
*
* 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 <string>
#include <osg/TexEnv>
#include <osg/CullFace>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Material>
#include <osg/Image>
#include <osg/Texture2D>
#include <osg/Notify>
#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgDB/FileNameUtils>
/**
* 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<ReaderWriterDirectX> 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<osg::Geometry*> 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;
}

View File

@ -0,0 +1,633 @@
// -*-c++-*-
/*
* $Id$
*
* Loader for DirectX .x files.
* Copyright (c)2002 Ulrich Hertlein <u.hertlein@sandbox.de>
*
* 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 <iostream>
#include <sstream>
#include <math.h>
#include <assert.h>
using namespace DX;
using namespace std;
// Tokenize a string
void tokenize(const string& str, vector<string>& 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<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<string> 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<string> 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<Coords2d>& v, unsigned int count)
{
char buf[256];
vector<string> 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<string> 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<unsigned int>& v, unsigned int count)
{
char buf[256];
vector<string> 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<string> 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<Vector>& v, unsigned int count)
{
char buf[256];
vector<string> 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<MeshFace>& v, unsigned int count)
{
char buf[256];
vector<string> 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<string> 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<string> 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<string> 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);
}
}
}

View File

@ -0,0 +1,195 @@
// -*-c++-*-
/*
* $Id$
*
* Loader for DirectX .x files.
* Copyright (c)2002 Ulrich Hertlein <u.hertlein@sandbox.de>
*
* 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 <string>
#include <vector>
#include <fstream>
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<TextureFilename> texture;
} Material;
// MeshFace
typedef std::vector<unsigned int> MeshFace;
// MeshTextureCoords
typedef std::vector<Coords2d> MeshTextureCoords;
// MeshNormals
typedef struct {
std::vector<Vector> normals;
std::vector<MeshFace> faceNormals;
} MeshNormals;
// MeshVertexColors.
typedef std::vector<IndexedColor> MeshVertexColors;
// MeshMaterialList
typedef struct {
std::vector<unsigned int> faceIndices;
std::vector<Material> material;
} MeshMaterialList;
// Mesh
typedef struct {
std::vector<Vector> vertices;
std::vector<MeshFace> 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<Coords2d>& v, unsigned int count);
/// Read 'MeshTextureCoords'.
void readMeshTexCoords(std::ifstream& fin);
/// Read index list.
void readIndexList(ifstream& fin, std::vector<unsigned int>& v, unsigned int count);
/// Parse 'MeshMaterialList'.
void parseMeshMaterialList(std::ifstream& fin);
/// Read 'Vector'.
void readVector(ifstream& fin, std::vector<Vector>& v, unsigned int count);
/// Read 'MeshFace'.
void readMeshFace(ifstream& fin, std::vector<MeshFace>& 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