From Ulrich Hertlein, DirectX .x model loader plugin.
This commit is contained in:
633
src/osgPlugins/directx/directx.cpp
Normal file
633
src/osgPlugins/directx/directx.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user