root/OpenSceneGraph/trunk/src/osgPlugins/x/ReaderWriterDirectX.cpp @ 13041

Revision 13041, 13.7 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1// -*-c++-*-
2
3/*
4 * $Id$
5 *
6 * DirectX file converter for OpenSceneGraph.
7 * Copyright (c)2002 Ulrich Hertlein <u.hertlein@sandbox.de>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 */
23
24#include "directx.h"
25
26#include <osg/TexEnv>
27#include <osg/CullFace>
28
29#include <osg/Geode>
30#include <osg/Geometry>
31#include <osg/Material>
32#include <osg/Image>
33#include <osg/Texture2D>
34
35#include <osg/Notify>
36#include <osgDB/Registry>
37#include <osgDB/ReadFile>
38#include <osgDB/FileNameUtils>
39#include <osgDB/FileUtils>
40
41#include <map>
42#include <iostream>
43
44
45/**
46 * OpenSceneGraph plugin wrapper/converter.
47 */
48class ReaderWriterDirectX : public osgDB::ReaderWriter
49{
50public:
51    ReaderWriterDirectX()
52    {
53        supportsExtension("x","DirectX scene format");
54        supportsOption("flipTexture", "flip texture upside-down");
55        // made hand switching an option - .x models from XSI's export are right-handed already
56        supportsOption("rightHanded", "prevents reader from switching handedness for right handed files");
57        supportsOption("leftHanded", "reader switches handedness for left handed files");
58    }
59
60    virtual const char* className() const {
61        return "DirectX Reader";
62    }
63
64    virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const;
65    virtual ReadResult readNode(std::istream& fin, const osgDB::ReaderWriter::Options* options) const;
66
67private:
68    osg::Group * convertFromDX(DX::Object & obj, bool switchToLeftHanded, bool flipTexture, float creaseAngle,
69            const osgDB::ReaderWriter::Options * options) const;
70
71    osg::Geode * convertFromDX(DX::Mesh & mesh, bool switchToLeftHanded, bool flipTexture, float creaseAngle,
72            const osgDB::ReaderWriter::Options * options) const;
73};
74
75// Register with Registry to instantiate the above reader/writer.
76REGISTER_OSGPLUGIN(x, ReaderWriterDirectX)
77
78
79// Read node
80osgDB::ReaderWriter::ReadResult ReaderWriterDirectX::readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const
81{
82    std::string ext = osgDB::getLowerCaseFileExtension(file);
83    if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
84
85    std::string fileName = osgDB::findDataFile( file, options );
86    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
87
88    OSG_INFO << "ReaderWriterDirectX::readNode(" << fileName << ")\n";
89
90    osgDB::ifstream fin(fileName.c_str());
91    if (fin.bad()) {
92        OSG_WARN << "ReaderWriterDirectX failed to read '" << fileName.c_str() << "'\n";
93        return ReadResult::ERROR_IN_READING_FILE;
94    }
95
96    // code for setting up the database path so that internally referenced file are searched for on relative paths.
97    osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
98    local_opt->setDatabasePath(osgDB::getFilePath(fileName));
99
100    return readNode(fin, local_opt.get());
101}
102
103osgDB::ReaderWriter::ReadResult ReaderWriterDirectX::readNode(std::istream& fin, const osgDB::ReaderWriter::Options* options) const
104{
105    DX::Object obj;
106    if (obj.load(fin) == false) {
107        OSG_WARN << "ReaderWriterDirectX failed to read stream" << std::endl;
108        return ReadResult::ERROR_IN_READING_FILE;
109    }
110
111    // Options?
112    bool flipTexture = true;
113    bool switchToLeftHanded = true; // when true: swap y and z for incoming files
114    float creaseAngle = 80.0f;
115    if (options) {
116        const std::string option = options->getOptionString();
117        if (option.find("rightHanded") != std::string::npos) {
118            switchToLeftHanded = false;
119        }
120        if (option.find("leftHanded") != std::string::npos) {
121            switchToLeftHanded = true;
122        }
123        if (option.find("flipTexture") != std::string::npos) {
124            flipTexture = false;
125        }
126        if (option.find("creaseAngle") != std::string::npos) {
127            // TODO
128        }
129    }
130
131    // Convert to osg::Group
132    osg::Group* group = convertFromDX(obj, switchToLeftHanded, flipTexture, creaseAngle, options);
133    if (!group) {
134        OSG_WARN << "ReaderWriterDirectX failed to convert\n";
135        return ReadResult::ERROR_IN_READING_FILE;
136    }
137
138    return group;
139}
140
141
142// Convert DirectX object
143osg::Group * ReaderWriterDirectX::convertFromDX(DX::Object & obj, bool switchToLeftHanded,
144                                                bool flipTexture, float creaseAngle,
145                                                const osgDB::ReaderWriter::Options * options) const
146{
147    osg::ref_ptr<osg::Group> group = new osg::Group;
148
149    for (unsigned int i = 0; i < obj.getNumMeshes(); ++i) {
150        //std::cerr << "converting mesh " << i << std::endl;
151        DX::Mesh & mesh = *obj.getMesh(i);
152        osg::Geode * geode = convertFromDX(mesh, switchToLeftHanded, flipTexture, creaseAngle, options);
153        if (!geode) {
154            return 0;
155        }
156        group->addChild(geode);
157    }
158
159    return group.release();
160}
161
162// Convert DirectX mesh to osg::Geode
163osg::Geode* ReaderWriterDirectX::convertFromDX(DX::Mesh & mesh, bool switchToLeftHanded,
164                                               bool flipTexture, float creaseAngle,
165                                               const osgDB::ReaderWriter::Options * options) const
166{
167    const DX::MeshMaterialList* meshMaterial = mesh.getMeshMaterialList();
168    if (!meshMaterial)
169        return NULL;
170
171    const DX::MeshNormals* meshNormals = mesh.getMeshNormals();
172    if (!meshNormals) {
173        mesh.generateNormals(creaseAngle);
174        meshNormals = mesh.getMeshNormals();
175    }
176    //std::cerr << "normals=" << meshNormals << std::endl;
177    if (!meshNormals)
178        return NULL;
179
180    const DX::MeshTextureCoords* meshTexCoords = mesh.getMeshTextureCoords();
181    //std::cerr << "texcoord=" << meshTexCoords << std::endl;
182
183    /*
184     * - MeshMaterialList contains a list of Material and a per-face
185     *   information with Material is to be applied to which face.
186     * - Mesh contains a list of Vertices and a per-face information
187     *   which vertices (three or four) belong to this face.
188     * - MeshNormals contains a list of Normals and a per-face information
189     *   which normal is used by which vertex.
190     * - MeshTextureCoords contains a list of per-vertex texture coordinates.
191     *
192     * - Uses left-hand CS with Y-up, Z-into
193     *   obj_x -> osg_x
194     *   obj_y -> osg_z
195     *   obj_z -> osg_y
196     *
197     * - aa: Changed always change left to right hand to an option that allows
198     *   us to read right-handed models as-is.  Our modeler is using XSI, which
199     *   exports to right-handed system.
200     *
201     * - Polys are CW oriented
202     */
203    std::vector<osg::Geometry*> geomList;
204
205    // Texture-for-Image map
206    std::map<std::string, osg::Texture2D*> texForImage;
207
208    unsigned int i;
209    for (i = 0; i < meshMaterial->material.size(); i++) {
210
211        //std::cerr << "material " << i << std::endl;
212
213        const DX::Material& mtl = meshMaterial->material[i];
214        osg::StateSet* state = new osg::StateSet;
215
216        // Material
217        osg::Material* material = new osg::Material;
218        state->setAttributeAndModes(material);
219
220        float alpha = mtl.faceColor.alpha;
221        osg::Vec4 ambient(mtl.faceColor.red,
222                          mtl.faceColor.green,
223                          mtl.faceColor.blue,
224                          alpha);
225        material->setAmbient(osg::Material::FRONT, ambient);
226        material->setDiffuse(osg::Material::FRONT, ambient);
227
228        material->setShininess(osg::Material::FRONT, mtl.power);
229
230        osg::Vec4 specular(mtl.specularColor.red,
231                           mtl.specularColor.green,
232                           mtl.specularColor.blue, alpha);
233        material->setSpecular(osg::Material::FRONT, specular);
234
235        osg::Vec4 emissive(mtl.emissiveColor.red,
236                           mtl.emissiveColor.green,
237                           mtl.emissiveColor.blue, alpha);
238        material->setEmission(osg::Material::FRONT, emissive);
239
240        // Transparency? Set render hint & blending
241        if (alpha < 1.0f) {
242            state->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
243            state->setMode(GL_BLEND, osg::StateAttribute::ON);
244        }
245        else
246            state->setMode(GL_BLEND, osg::StateAttribute::OFF);
247
248        unsigned int textureCount = mtl.texture.size();
249        for (unsigned int j = 0; j < textureCount; j++) {
250
251            //std::cerr << "texture " << j << std::endl;
252
253            // Share image/texture pairs
254            osg::Texture2D* texture = texForImage[mtl.texture[j]];
255            if (!texture) {
256                osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(mtl.texture[j],options);
257                if (!image)
258                    continue;
259
260                // Texture
261                texture = new osg::Texture2D;
262                texForImage[mtl.texture[j]] = texture;
263
264                texture->setImage(image.get());
265                texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT);
266                texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::REPEAT);
267            }
268            state->setTextureAttributeAndModes(j, texture);
269        }
270
271        // Geometry
272        osg::Geometry* geom = new osg::Geometry;
273        geomList.push_back(geom);
274
275        geom->setStateSet(state);
276
277        // Arrays to hold vertices, normals, and texcoords.
278        geom->setVertexArray(new osg::Vec3Array);
279        geom->setNormalArray(new osg::Vec3Array);
280        geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
281        if (textureCount) {
282            // All texture units share the same array
283            osg::Vec2Array* texCoords = new osg::Vec2Array;
284            for (unsigned int j = 0; j < textureCount; j++)
285                geom->setTexCoordArray(j, texCoords);
286        }
287
288        geom->addPrimitiveSet(new osg::DrawArrayLengths(osg::PrimitiveSet::POLYGON));
289    }
290
291    const std::vector<DX::MeshFace> & faces = mesh.getFaces();
292    if (faces.size() != meshMaterial->faceIndices.size())
293    {
294        OSG_FATAL<<"Error: internal error in DirectX .x loader,"<<std::endl;
295        OSG_FATAL<<"       mesh->faces.size() == meshMaterial->faceIndices.size()"<<std::endl;
296        return NULL;
297    }
298
299    // Add faces to Geometry
300    for (i = 0; i < meshMaterial->faceIndices.size(); i++) {
301
302        // Geometry for Material
303        unsigned int mi = meshMaterial->faceIndices[i];
304        osg::Geometry* geom = geomList[mi];
305
306        // #pts of this face
307        unsigned int np = faces[i].size();
308        ((osg::DrawArrayLengths*) geom->getPrimitiveSet(0))->push_back(np);
309
310        if (np != meshNormals->faceNormals[i].size())
311        {
312            OSG_WARN<<"DirectX loader: Error, error in normal list."<<std::endl;
313        }
314
315        osg::Vec3Array* vertexArray = (osg::Vec3Array*) geom->getVertexArray();
316        osg::Vec3Array* normalArray = (osg::Vec3Array*) geom->getNormalArray();
317        osg::Vec2Array* texCoordArray=NULL; // only make them if the original has them
318        if(meshTexCoords) texCoordArray = (osg::Vec2Array*) geom->getTexCoordArray(0);
319
320        // Add vertices, normals, texcoords
321        for (unsigned int j = 0; j < np; j++) {
322
323            // Convert CW to CCW order
324            unsigned int jj = (j > 0 ? np - j : j);
325            if(!switchToLeftHanded) jj=j;
326
327            // Vertices
328            unsigned int vi = faces[i][jj];
329            if (vertexArray) {
330                const DX::Vector & v = mesh.getVertices()[vi];
331                if(switchToLeftHanded)// Transform Xleft/Yup/Zinto to Xleft/Yinto/Zup
332                    vertexArray->push_back(osg::Vec3(v.x,v.z,v.y));
333                else
334                    vertexArray->push_back(osg::Vec3(v.x,v.y,v.z));
335            }
336
337            // Normals
338            unsigned int ni = meshNormals->faceNormals[i][jj];
339            if (normalArray) {
340                const DX::Vector& n = meshNormals->normals[ni];
341                if(switchToLeftHanded)// Transform Xleft/Yup/Zinto to Xleft/Yinto/Zup
342                    normalArray->push_back(osg::Vec3(n.x,n.z,n.y));
343                else
344                    normalArray->push_back(osg::Vec3(n.x,n.y,n.z));
345            }
346
347            // TexCoords
348            if (texCoordArray) {
349                const DX::Coords2d& tc = (*meshTexCoords)[vi];
350                osg::Vec2 uv;
351                if (flipTexture){
352                    if(switchToLeftHanded)
353                        uv.set(tc.u, 1.0f - tc.v); // Image is upside down
354                    else
355                        uv.set(1.0f - tc.u, 1.0f - tc.v); // Image is 180 degrees
356                }
357                else
358                    uv.set(tc.u, tc.v);
359                texCoordArray->push_back(uv);
360            }
361        }
362    }
363
364    // Add non-empty nodes to Geode
365    osg::Geode* geode = new osg::Geode;
366    for (i = 0; i < geomList.size(); i++) {
367        osg::Geometry* geom = geomList[i];
368        if (((osg::Vec3Array*) geom->getVertexArray())->size())
369            geode->addDrawable(geom);
370    }
371
372    // Back-face culling
373    osg::StateSet* state = new osg::StateSet;
374    geode->setStateSet(state);
375
376    osg::CullFace* cullFace = new osg::CullFace;
377    cullFace->setMode(osg::CullFace::BACK);
378    state->setAttributeAndModes(cullFace);
379
380    return geode;
381}
Note: See TracBrowser for help on using the browser.