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

Reply via email to