Please see attached ReaderWriterDirectX.cpp for the changes needed to add options leftHanded and rightHanded. In this reader, the default is rightHanded since that's what I'm using. The published version should probably change switchHands to true to make leftHanded the default. I don't think there is any flag in the .x spec for left or right handed models. From what I can tell, this is pretty specific to the XSI exporter and may be specific to the particular version of XSI that our modeler is using. In general, it seems to just be up to the programmer to know what system the model is in and use it accordingly.
As for providing a file that uses the FrameTransformMatrix, I haven't gotten permission from our modeler to use his models, so am looking for an example in Microsoft's SDK that has the nodes. For now, an example that I hand-edited so it has one colored box and a second smaller box at an offset - http://crazysweet.net/two_boxes.x The osg loader draws the two boxes on top of each other, so it looks like only one. The directX Viewer shows the two distinct boxes. I won't end up needing to use transform matrices for the current project - I've been able to generate the model I need using osgconv and the pseudo-loaders, exporting to osg native files. Thanks, Aric ________________________________ From: Ulrich Hertlein <[email protected]> To: OpenSceneGraph Submissions <[email protected]> Sent: Tuesday, March 24, 2009 5:38:30 PM Subject: Re: [osg-submissions] DirectX plugin minor fixes Hi Aric, On 25/3/09 3:24 AM, Aric Aumann wrote: > According to this spec, "the frame can contain objects of the type Mesh > and a FrameTransformMatrix." I might end up needing to handle the > matrices, but at this point I think it's more likely that our modeler > can just export models without the matrices, and the parseSection will > be good enough as-is. Can you make one or more of these models available for download in case someone wants to tackle this issue? Unless of course there's an I.P. issue... > I've added an option on our local build that makes the handedness swap > in ReaderWriterDirectX.cpp an option. Our modeler is using XSI, which > has a DirectX export but it puts out right handed coordinates, so the > hand swap isn't necessary. I don't know how common of an issue this is, > but can provide my changes. Is there some flag in the .x file that specifies right-hand vs. left-hand CS? I was under the assumption that .x files are *always* left-handed (because the rest of DX is). The obj loader supports a similar option (called 'noRotation' which maybe isn't the best choice) so I'd say go ahead and submit it. (Also saves you to worry about conflicting changes.) <rant> This is sheer madness: at least the spec says what it's storing, but hey, that's not stopping vendors from happily fscking the spec without a way to detect this at load time. </rant> Cheers, /ulrich _______________________________________________ osg-submissions mailing list [email protected] http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
// -*-c++-*- /* * $Id: ReaderWriterDirectX.cpp 9527 2009-01-21 18:23:55Z robert $ * * DirectX file converter for OpenSceneGraph. * Copyright (c)2002 Ulrich Hertlein <[email protected]> * * 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 <map> #include <iostream> /** * OpenSceneGraph plugin wrapper/converter. */ class ReaderWriterDirectX : public osgDB::ReaderWriter { public: ReaderWriterDirectX() { supportsExtension("x","DirectX scene format"); supportsOption("flipTexture", "flip texture upside-down"); // made hand switching an option - .x models from XSI's export are right-handed already supportsOption("rightHanded", "prevents reader from switching handedness for right handed files"); supportsOption("leftHanded", "reader switches handedness for left handed files"); } virtual const char* className() const { return "DirectX Reader"; } virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const; private: osg::Group * convertFromDX(DX::Object & obj, bool switchHands, bool flipTexture, float creaseAngle, const osgDB::ReaderWriter::Options * options) const; osg::Geode * convertFromDX(DX::Mesh & mesh, bool switchHands, bool flipTexture, float creaseAngle, const osgDB::ReaderWriter::Options * options) const; }; // Register with Registry to instantiate the above reader/writer. REGISTER_OSGPLUGIN(x, ReaderWriterDirectX) // Read node osgDB::ReaderWriter::ReadResult ReaderWriterDirectX::readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const { std::string ext = osgDB::getLowerCaseFileExtension(file); if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; std::string fileName = osgDB::findDataFile( file, options ); 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()) == false) { return ReadResult::ERROR_IN_READING_FILE; } // code for setting up the database path so that internally referenced file are searched for on relative paths. osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options; local_opt->setDatabasePath(osgDB::getFilePath(fileName)); // Options? bool flipTexture = true; bool switchHands = false; // when true: swap y and z for incoming files float creaseAngle = 80.0f; if (options) { const std::string option = options->getOptionString(); if(option.find("rightHanded") != std::string::npos) { switchHands=false; } if(option.find("leftHanded") != std::string::npos) { switchHands=true; } if (option.find("flipTexture") != std::string::npos) { flipTexture = false; } if (option.find("creaseAngle") != std::string::npos) { // TODO } } // Convert to osg::Group osg::Group* group = convertFromDX(obj, switchHands, flipTexture, creaseAngle, local_opt.get()); if (!group) { return ReadResult::ERROR_IN_READING_FILE; } return group; } // Convert DirectX object osg::Group * ReaderWriterDirectX::convertFromDX(DX::Object & obj, bool switchHands, bool flipTexture, float creaseAngle, const osgDB::ReaderWriter::Options * options) const { osg::ref_ptr<osg::Group> group = new osg::Group; for (unsigned int i = 0; i < obj.getNumMeshes(); ++i) { //std::cerr << "converting mesh " << i << std::endl; DX::Mesh & mesh = *obj.getMesh(i); osg::Geode * geode = convertFromDX(mesh, switchHands, flipTexture, creaseAngle, options); if (!geode) { return 0; } group->addChild(geode); } return group.release(); } // Convert DirectX mesh to osg::Geode osg::Geode* ReaderWriterDirectX::convertFromDX(DX::Mesh & mesh, bool switchHands, bool flipTexture, float creaseAngle, const osgDB::ReaderWriter::Options * options) const { const DX::MeshMaterialList* meshMaterial = mesh.getMeshMaterialList(); if (!meshMaterial) return NULL; const DX::MeshNormals* meshNormals = mesh.getMeshNormals(); if (!meshNormals) { mesh.generateNormals(creaseAngle); meshNormals = mesh.getMeshNormals(); } //std::cerr << "normals=" << meshNormals << std::endl; if (!meshNormals) return NULL; const DX::MeshTextureCoords* meshTexCoords = mesh.getMeshTextureCoords(); //std::cerr << "texcoord=" << meshTexCoords << std::endl; /* * - 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 * * - aa: Changed always change left to right hand to an option that allows * us to read right-handed models as-is. Our modeler is using XSI, which * exports to right-handed system. * * - 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++) { //std::cerr << "material " << i << std::endl; 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++) { //std::cerr << "texture " << j << std::endl; // Share image/texture pairs osg::Texture2D* texture = texForImage[mtl.texture[j]]; if (!texture) { osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(mtl.texture[j],options); if (!image) continue; // Texture texture = new osg::Texture2D; texForImage[mtl.texture[j]] = texture; texture->setImage(image.get()); 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)); } const std::vector<DX::MeshFace> & faces = mesh.getFaces(); if (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 = faces[i].size(); ((osg::DrawArrayLengths*) geom->getPrimitiveSet(0))->push_back(np); if (np != meshNormals->faceNormals[i].size()) { osg::notify(osg::WARN)<<"DirectX loader: Error, error in normal list."<<std::endl; } osg::Vec3Array* vertexArray = (osg::Vec3Array*) geom->getVertexArray(); osg::Vec3Array* normalArray = (osg::Vec3Array*) geom->getNormalArray(); osg::Vec2Array* texCoordArray=NULL; // only make them if the original has them if(meshTexCoords) 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); if(!switchHands) jj=j; // Vertices unsigned int vi = faces[i][jj]; if (vertexArray) { const DX::Vector & v = mesh.getVertices()[vi]; if(switchHands)// Transform Xleft/Yup/Zinto to Xleft/Yinto/Zup vertexArray->push_back(osg::Vec3(v.x,v.z,v.y)); else vertexArray->push_back(osg::Vec3(v.x,v.y,v.z)); } // Normals unsigned int ni = meshNormals->faceNormals[i][jj]; if (normalArray) { const DX::Vector& n = meshNormals->normals[ni]; if(switchHands)// Transform Xleft/Yup/Zinto to Xleft/Yinto/Zup normalArray->push_back(osg::Vec3(n.x,n.z,n.y)); else normalArray->push_back(osg::Vec3(n.x,n.y,n.z)); } // TexCoords if (texCoordArray) { const DX::Coords2d& tc = (*meshTexCoords)[vi]; osg::Vec2 uv; if (flipTexture){ if(switchHands) uv.set(tc.u, 1.0f - tc.v); // Image is upside down else uv.set(1.0f - tc.u, 1.0f - tc.v); // Image is 180 degrees } 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; }
_______________________________________________ osg-submissions mailing list [email protected] http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
