instead of osgDB::Registry where it original lay. This has been done to allow fileName strings to be encode data rather than just file names, such as one requires when using PagedLOD along with plugins for doing dynamic tesselation.
313 lines
10 KiB
C++
313 lines
10 KiB
C++
// -*-c++-*-
|
|
|
|
/*
|
|
* $Id$
|
|
*
|
|
* DirectX file converter for OpenSceneGraph.
|
|
* Copyright (c)2002 Ulrich Hertlein <u.hertlein@sandbox.de>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "directx.h"
|
|
|
|
#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>
|
|
#include <osgDB/FileUtils>
|
|
|
|
#include <assert.h>
|
|
#include <map>
|
|
|
|
|
|
/**
|
|
* 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, float creaseAngle);
|
|
};
|
|
|
|
// 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& file,
|
|
const osgDB::ReaderWriter::Options* options)
|
|
{
|
|
std::string ext = osgDB::getLowerCaseFileExtension(file);
|
|
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
|
|
|
|
std::string fileName = osgDB::findDataFile( file );
|
|
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
|
|
|
|
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;
|
|
float creaseAngle = 80.0f;
|
|
if (options) {
|
|
const std::string option = options->getOptionString();
|
|
if (option.find("flipTexture") != std::string::npos)
|
|
flipTexture = false;
|
|
if (option.find("creaseAngle") != std::string::npos) {
|
|
// TODO
|
|
}
|
|
}
|
|
|
|
// Convert to osg::Geode
|
|
osg::Geode* geode = convertFromDX(obj, flipTexture, creaseAngle);
|
|
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, float creaseAngle)
|
|
{
|
|
// 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(creaseAngle);
|
|
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
|
|
*/
|
|
std::vector<osg::Geometry*> geomList;
|
|
|
|
// Texture-for-Image map
|
|
std::map<std::string, osg::Texture2D*> texForImage;
|
|
|
|
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++) {
|
|
|
|
// Share image/texture pairs
|
|
osg::Texture2D* texture = texForImage[mtl.texture[j]];
|
|
if (!texture) {
|
|
osg::Image* image = osgDB::readImageFile(mtl.texture[j]);
|
|
if (!image)
|
|
continue;
|
|
|
|
// Texture
|
|
texture = new osg::Texture2D;
|
|
texForImage[mtl.texture[j]] = texture;
|
|
|
|
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));
|
|
}
|
|
|
|
if (mesh->faces.size() != meshMaterial->faceIndices.size())
|
|
{
|
|
osg::notify(osg::FATAL)<<"Error: internal error in DirectX .x loader,"<<std::endl;
|
|
osg::notify(osg::FATAL)<<" mesh->faces.size() == meshMaterial->faceIndices.size()"<<std::endl;
|
|
return NULL;
|
|
}
|
|
|
|
// 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);
|
|
|
|
return geode;
|
|
}
|