Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/ogre/Tools/XSIExport/src/OgreXSIMeshExporter.cpp @ 52

Last change on this file since 52 was 6, checked in by anonymous, 17 years ago

=…

File size: 55.3 KB
Line 
1/*
2-----------------------------------------------------------------------------
3This source file is part of OGRE
4(Object-oriented Graphics Rendering Engine)
5For the latest info, see http://www.ogre3d.org/
6
7Copyright (c) 2000-2006 Torus Knot Software Ltd
8Also see acknowledgements in Readme.html
9
10This program is free software; you can redistribute it and/or modify it under
11the terms of the GNU Lesser General Public License as published by the Free Software
12Foundation; either version 2 of the License, or (at your option) any later
13version.
14
15This program is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
18
19You should have received a copy of the GNU Lesser General Public License along with
20this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21Place - Suite 330, Boston, MA 02111-1307, USA, or go to
22http://www.gnu.org/copyleft/lesser.txt.
23
24You may alternatively use this source under the terms of a specific version of
25the OGRE Unrestricted License provided you have obtained such a license from
26Torus Knot Software Ltd.
27-----------------------------------------------------------------------------
28*/
29#include "OgreXSIMeshExporter.h"
30#include <xsi_model.h>
31#include <xsi_primitive.h>
32#include <xsi_polygonnode.h>
33#include <xsi_material.h>
34#include <xsi_vertex.h>
35#include <xsi_trianglevertex.h>
36#include <xsi_cluster.h>
37#include <xsi_kinematics.h>
38#include <xsi_kinematicstate.h>
39#include <xsi_selection.h>
40#include <xsi_envelope.h>
41#include <xsi_time.h>
42#include <xsi_source.h>
43#include <xsi_edge.h>
44#include <xsi_vector3.h>
45#include <xsi_matrix4.h>
46#include <xsi_mixer.h>
47#include <xsi_clip.h>
48#include <xsi_timecontrol.h>
49#include <xsi_actionsource.h>
50#include <xsi_fcurve.h>
51#include <xsi_fcurvekey.h>
52
53#include "OgreException.h"
54#include "OgreXSIHelper.h"
55#include "OgreLogManager.h"
56#include "OgreMeshManager.h"
57#include "OgreMesh.h"
58#include "OgreSubMesh.h"
59#include "OgreMeshManager.h"
60#include "OgreMeshSerializer.h"
61#include "OgreHardwareBufferManager.h"
62#include "OgreVertexBoneAssignment.h"
63#include "OgrePose.h"
64#include "OgreAnimation.h"
65#include "OgreAnimationTrack.h"
66
67using namespace XSI;
68
69
70namespace Ogre {
71    //-----------------------------------------------------------------------
72    XsiMeshExporter::UniqueVertex::UniqueVertex()
73        : position(Vector3::ZERO), normal(Vector3::ZERO), colour(0), nextIndex(0)
74    {
75        for (int i = 0; i < OGRE_MAX_TEXTURE_COORD_SETS; ++i)
76            uv[i] = Vector3::ZERO;
77    }
78    //-----------------------------------------------------------------------
79    bool XsiMeshExporter::UniqueVertex::operator==(const UniqueVertex& rhs) const
80    {
81        bool ret = position == rhs.position && 
82            normal == rhs.normal && 
83            colour == rhs.colour;
84        if (!ret) return ret;
85
86        for (int i = 0; i < OGRE_MAX_TEXTURE_COORD_SETS && ret; ++i)
87        {
88            ret = ret && (uv[i] == rhs.uv[i]);
89        }
90
91        return ret;
92       
93
94    }
95    //-----------------------------------------------------------------------
96    //-----------------------------------------------------------------------
97    XsiMeshExporter::XsiMeshExporter()
98    {
99    }
100    //-----------------------------------------------------------------------
101    XsiMeshExporter::~XsiMeshExporter()
102    {
103                /// Tidy up
104                cleanupDeformerMap();
105                cleanupMaterialMap();
106    }
107    //-----------------------------------------------------------------------
108        DeformerMap& XsiMeshExporter::buildMeshForExport(
109                bool mergeSubMeshes, bool exportChildren, 
110                bool edgeLists, bool tangents, VertexElementSemantic tangentSemantic, 
111                bool vertexAnimation, AnimationList& animList, Real fps, const String& materialPrefix, 
112                LodData* lod, const String& skeletonName)
113    {
114
115                LogOgreAndXSI(L"** Begin OGRE Mesh Export **");
116        // Derive the scene root
117        X3DObject sceneRoot(mXsiApp.GetActiveSceneRoot());
118
119        // Construct mesh
120        mMesh = MeshManager::getSingleton().createManual("XSIExport", 
121                        ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
122
123                mMaterialPrefix = materialPrefix;
124
125                cleanupDeformerMap();
126                cleanupMaterialMap();
127                mShapeKeyMapping.clear();
128
129                // Find all PolygonMesh objects
130                buildPolygonMeshList(exportChildren);
131                // progress report
132                ProgressManager::getSingleton().progress();
133
134                // notify of skeleton beforehand
135                if (!skeletonName.empty())
136                {
137                        mMesh->setSkeletonName(skeletonName);
138                }
139
140                // write the data into a mesh
141                buildMesh(mMesh.getPointer(), mergeSubMeshes, !skeletonName.empty(), 
142                        vertexAnimation, animList, fps);
143
144                // progress report
145                ProgressManager::getSingleton().progress();
146
147                if (lod)
148                {
149                        mMesh->generateLodLevels(lod->distances, lod->quota, lod->reductionValue);
150                        // progress report
151                        ProgressManager::getSingleton().progress();
152                }
153
154        if(edgeLists)
155        {
156            LogOgreAndXSI(L"Calculating edge lists");
157            mMesh->buildEdgeList();
158                        // progress report
159                        ProgressManager::getSingleton().progress();
160        }
161
162        if(tangents)
163        {
164            LogOgreAndXSI(L"Calculating tangents");
165            unsigned short src, dest;
166            if (!mMesh->suggestTangentVectorBuildParams(tangentSemantic, src, dest))
167            {
168                mMesh->buildTangentVectors(tangentSemantic, src, dest);
169            }
170            else
171            {
172                LogOgreAndXSI(L"Could not derive tangents parameters");
173            }
174                        // progress report
175                        ProgressManager::getSingleton().progress();
176
177        }
178
179                cleanupPolygonMeshList();
180
181                LogOgreAndXSI(L"** OGRE Mesh Export Complete **");
182
183                return mXsiDeformerMap;
184    }
185        //-----------------------------------------------------------------------
186        void XsiMeshExporter::exportMesh(const String& fileName, const AxisAlignedBox& skelAABB)
187        {
188
189                // Pad bounds
190                AxisAlignedBox currBounds = mMesh->getBounds();
191                currBounds.merge(skelAABB);
192                mMesh->_setBounds(currBounds, false);
193
194                MeshSerializer serializer;
195                serializer.exportMesh(mMesh.getPointer(), fileName);
196
197                // progress report
198                ProgressManager::getSingleton().progress();
199
200                mMesh.setNull();
201
202
203        }
204        //-----------------------------------------------------------------------
205        MaterialMap& XsiMeshExporter::getMaterials(void)
206        {
207                return mXsiMaterialMap;
208        }
209        //-----------------------------------------------------------------------
210        TextureProjectionMap& XsiMeshExporter::getTextureProjectionMap(void)
211        {
212                return mTextureProjectionMap;
213        }
214        //-----------------------------------------------------------------------
215        void XsiMeshExporter::buildMesh(Mesh* pMesh, bool mergeSubmeshes, 
216                bool lookForBoneAssignments, bool vertexAnimation, AnimationList& animList, 
217                Real fps)
218        {
219                /* Iterate over the list of polygon meshes that we've already located.
220                        For each one:
221                                If we're not merging submeshes, bake any protosubmeshes built
222                                  into the mesh and clear the protosubmesh list
223                            Scan the clusters for 'poly' clusters (which can contain material
224                                  discrepancies). Any that use a different material should be
225                                  noted in the polycluster list
226                            Build ProtoSubMeshes by iterating over the triangles in the mesh,
227                                  building the list of UniqueVertices against the material as we
228                                  go. We check each triangle to see if the polygon index is in
229                                  the list of polyclusters & if so it goes into the other lists
230                   
231                        Finally, we bake any remaining protosubmeshes into submeshes.
232                */
233                // Calculate the number of progress updates each mesh must raise
234                float progPerMesh = (float)OGRE_XSI_NUM_MESH_STEPS / (float)(mXsiPolygonMeshList.size());
235                float currProg = 0.0f;
236                for (PolygonMeshList::iterator pm = mXsiPolygonMeshList.begin();
237                        pm != mXsiPolygonMeshList.end(); ++pm)
238                {
239                        currProg += progPerMesh;
240                        unsigned short progUpdates = (unsigned short)currProg;
241                        currProg -= progUpdates;
242                        // build contents of this polymesh into ProtoSubMesh(es)
243                        processPolygonMesh(pMesh, *pm, lookForBoneAssignments, progUpdates);
244
245                        if (!mergeSubmeshes)
246                        {
247                                // export out at the end of every PolygonMesh
248                                exportProtoSubMeshes(pMesh);
249                        }
250                }
251                if (mergeSubmeshes)
252                {
253                        // export out the combined result
254                        exportProtoSubMeshes(pMesh);
255                }
256
257                if (vertexAnimation)
258                {
259                        exportAnimations(pMesh, animList, fps);
260                }
261        }
262        //-----------------------------------------------------------------------
263        XsiMeshExporter::ProtoSubMesh* XsiMeshExporter::createOrRetrieveProtoSubMesh(
264                const String& materialName, const String& name, 
265                TextureCoordDimensionList& texCoordDims, bool hasVertexColours)
266        {
267                bool createNew = true;
268                ProtoSubMesh* ret = 0;
269                ProtoSubMeshList* protoList = 0;
270               
271                MaterialProtoSubMeshMap::iterator pi = mMaterialProtoSubmeshMap.find(materialName);
272                if (pi == mMaterialProtoSubmeshMap.end())
273                {
274                        protoList = new ProtoSubMeshList();
275                        mMaterialProtoSubmeshMap[materialName] = protoList;
276                }
277                else
278                {
279                        // Iterate over the protos with the same material
280                        protoList = pi->second;
281
282                        for (ProtoSubMeshList::iterator psi = protoList->begin(); psi != protoList->end(); ++psi)
283                        {
284                                ProtoSubMesh* candidate = *psi;
285                                // Check format is compatible
286                                if (candidate->textureCoordDimensions.size() != texCoordDims.size())
287                                {
288                                        continue;
289                                }
290                                if (candidate->hasVertexColours != hasVertexColours)
291                                {
292                                        continue;
293                                }
294
295                                bool compat = true;
296                                std::vector<ushort>::iterator t = texCoordDims.begin();
297                                std::vector<ushort>::iterator u = candidate->textureCoordDimensions.begin(); 
298                                for (;t != texCoordDims.end(); ++t,++u)
299                                {
300                                        if (*t != *u)
301                                        {
302                                                compat = false;
303                                                break;
304                                        }
305                                }
306
307                                if (compat)
308                                {
309                                        createNew = false;
310                                        ret = candidate;
311                                        break;
312                                }
313                        }
314                }
315
316                if (createNew)
317                {
318                        ret = new ProtoSubMesh();
319                        protoList->push_back(ret);
320                        ret->materialName = materialName;
321                        ret->name = name;
322                        ret->textureCoordDimensions = texCoordDims;
323                        ret->hasVertexColours = hasVertexColours;
324                }
325
326                return ret;
327               
328        }
329        //-----------------------------------------------------------------------
330        void XsiMeshExporter::processPolygonMesh(Mesh* pMesh, PolygonMeshEntry* xsiMesh, 
331                bool lookForBoneAssignments, unsigned short progressUpdates)
332        {
333                // Pre-process the mesh
334                if (!preprocessPolygonMesh(xsiMesh))
335                {
336                        while(progressUpdates--)
337                                ProgressManager::getSingleton().progress();
338
339                        return;
340                }
341               
342        // Retrieve all the XSI relevant summary info
343        CPointRefArray pointArray(xsiMesh->mesh.GetPoints());
344        MATH::CVector3Array srcPosArray = pointArray.GetPositionArray();
345        CPolygonNodeRefArray nodeArray(xsiMesh->mesh.GetNodes());
346        MATH::CVector3Array srcNormArray = nodeArray.GetNormalArray();
347        CTriangleRefArray triArray = xsiMesh->mesh.GetTriangles();
348
349                StringUtil::StrStreamType msg;
350                msg << "-- " << XSItoOgre(xsiMesh->obj.GetName()) << " --" << std::endl;
351                msg << "Points: " << pointArray.GetCount() << std::endl;
352                msg << "Triangles: " << triArray.GetCount() << std::endl;
353                msg << "Normals: " << srcNormArray.GetCount() << std::endl;
354                msg << "Num UVs: " << mCurrentTextureCoordDimensions.size() << std::endl;
355                String str = msg.str();
356                LogOgreAndXSI(str);
357
358                if (mCurrentTextureCoordDimensions.size() > OGRE_MAX_TEXTURE_COORD_SETS)
359                {
360                        // too many texture coordinates!
361                        StringUtil::StrStreamType str;
362                        str << "PolygonMesh '" << XSItoOgre(xsiMesh->mesh.GetName()) 
363                                << "' has too many texture coordinate sets (" 
364                                << mCurrentTextureCoordDimensions.size()
365                                << "); the limit is " << OGRE_MAX_TEXTURE_COORD_SETS;
366
367                        OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, str.str(), 
368                                "XsiMeshExporter::processPolygonMesh");
369
370                }
371               
372                // Save transforms
373                MATH::CTransformation xsiTransform = xsiMesh->obj.GetKinematics().GetGlobal().GetTransform();
374                MATH::CTransformation rotTrans;
375                rotTrans.SetRotation(xsiTransform.GetRotation());
376                // Bounds calculation
377        Real squaredRadius = 0.0f;
378        Vector3 min, max;
379        bool first = true;
380                CPolygonFaceRefArray polys(xsiMesh->mesh.GetPolygons());
381        UniqueVertex vertex;
382
383
384                float progPerTri = (float)progressUpdates / triArray.GetCount();
385                float prog = 0.0f;
386                // Iterate through all the triangles
387        // There will often be less positions than normals and UVs
388        // But TrianglePoint
389        for (long t = 0; t < triArray.GetCount(); ++t)
390        {
391            Triangle tri(triArray[t]);
392                        // derive sampler indices for triangle
393                        size_t samplerIndices[3];
394                        deriveSamplerIndices(tri, polys[tri.GetPolygonIndex()], samplerIndices);
395
396                        // Decide which ProtoSubMesh we're to add this to (assume main)
397                        // If we find this triangle relates to a polygon which is in
398                        // a cluster which has a different material, we change
399                        ProtoSubMesh* currentProto = mMainProtoMesh;
400                        PolygonToProtoSubMeshList::iterator polyi = 
401                                mPolygonToProtoSubMeshList.find(tri.GetPolygonIndex());
402                        if (polyi != mPolygonToProtoSubMeshList.end())
403                        {
404                                currentProto = polyi->second;
405                        }
406                        // has this mesh been used in this proto before? if not set offset
407                        size_t positionIndexOffset;
408                        if (currentProto->lastMeshEntry == xsiMesh)
409                        {
410                                positionIndexOffset = currentProto->lastMeshIndexOffset;
411                        }
412                        else
413                        {
414                                // first time this has been used
415                                // since we assume we 100% process each polygon mesh before the next,
416                                // just use last pointer since faster in this section
417                                currentProto->lastMeshEntry = xsiMesh;
418                                positionIndexOffset = currentProto->indices.size();
419                                currentProto->lastMeshIndexOffset = positionIndexOffset;
420                                // Also have to store this for future reference
421                                currentProto->polygonMeshOffsetMap[xsiMesh] = positionIndexOffset;
422                        }
423                       
424            CTriangleVertexRefArray points = tri.GetPoints();
425            for (long p = 0; p < 3; ++p)
426            {
427                TriangleVertex point(points[p]);
428                long posIndex = point.GetIndex(); // unique position index
429                                // adjust index per offset, this makes position indices unique
430                                // per polymesh in teh same protosubmesh
431                                posIndex += positionIndexOffset;
432
433                                // Get position
434                                MATH::CVector3 xsipos = point.GetPosition();
435                                // Apply global SRT
436                                xsipos.MulByTransformationInPlace(xsiTransform);
437                vertex.position = XSItoOgre(xsipos);
438                                // Get normal
439                                MATH::CVector3 xsinorm = point.GetNormal();
440                                // Apply global rotation
441                                xsinorm *= rotTrans;
442                vertex.normal = XSItoOgre(xsinorm);
443
444                                for (size_t i = 0; i < mCurrentTextureCoordDimensions.size(); ++i)
445                                {
446                                        // sampler indices can correctly dereference to sampler-order
447                                        // uv sets we built earlier
448                                        vertex.uv[i] = (mCurrentSamplerSets[i])[samplerIndices[p]];
449                                }
450               
451                if (mCurrentHasVertexColours)
452                    vertex.colour = XSItoOgre(point.GetColor());
453
454                size_t index = createOrRetrieveUniqueVertex(
455                                                                        currentProto, posIndex, true, vertex);
456                currentProto->indices.push_back(index);
457
458                                // bounds
459                                if (first)
460                                {
461                                        squaredRadius = vertex.position.squaredLength();
462                                        min = max = vertex.position;
463                                        first = false;
464                                }
465                                else
466                                {
467                                        squaredRadius = 
468                                                std::max(squaredRadius, vertex.position.squaredLength());
469                                        min.makeFloor(vertex.position);
470                                        max.makeCeil(vertex.position);
471                                }
472            }
473               
474                        // Progress
475                        prog += progPerTri;
476                        while (prog >= 1.0f)
477                        {
478                                ProgressManager::getSingleton().progress();
479                                prog -= 1.0f;
480                        }
481
482                }
483
484                // Merge bounds
485                AxisAlignedBox box;
486        box.setExtents(min, max);
487        box.merge(pMesh->getBounds());
488        pMesh->_setBounds(box);
489        pMesh->_setBoundingSphereRadius(
490                        std::max(
491                                pMesh->getBoundingSphereRadius(), 
492                                Math::Sqrt(squaredRadius)));
493
494                // Deal with any bone assignments
495                if (lookForBoneAssignments)
496                {
497                        processBoneAssignments(pMesh, xsiMesh);
498                }
499
500                // process any shape keys
501                processShapeKeys(pMesh, xsiMesh);
502
503                // Post-process the mesh
504                postprocessPolygonMesh(xsiMesh);
505
506        }
507        //-----------------------------------------------------------------------
508        bool XsiMeshExporter::preprocessPolygonMesh(PolygonMeshEntry* xsiMesh)
509        {
510        // derive number of UVs
511        int numUVs = 0;
512        CRefArray clusterRefArray;
513        // Filter to 'sample' types
514        xsiMesh->mesh.GetClusters().Filter(
515                        siSampledPointCluster,CStringArray(),L"",clusterRefArray);
516
517        Cluster samplePointClusterUV;
518        CRefArray uvClusterPropertiesRefArray;
519                int i;
520       
521        for(i = 0; i < clusterRefArray.GetCount(); ++i)
522        {
523            Cluster cluster(clusterRefArray[i]);               
524            // Now filter all the 'uvspace' children
525            // there is one of these per UV set
526            if(cluster.GetProperties().Filter(
527                siClsUVSpaceTxtType, CStringArray(), L"", 
528                uvClusterPropertiesRefArray) == CStatus::OK)
529            {
530                samplePointClusterUV = cluster;                 
531                break;
532            }
533        }
534
535        // Ok, we now have our array of UV sets
536        numUVs = uvClusterPropertiesRefArray.GetCount();
537                size_t numSamplerPoints = xsiMesh->mesh.GetNodes().GetCount();
538                // list of UVs stored in order of sampler points (use 3D coords by default)
539                mCurrentSamplerSets.reserve(numUVs);
540                mCurrentTextureCoordDimensions.reserve(numUVs);
541        for(i = 0; i < numUVs; ++i)
542        {
543                        // init sampler points
544                        Vector3* samplerUVs = new Vector3[numSamplerPoints];
545                        mCurrentSamplerSets.push_back(samplerUVs);
546
547                        // Detect the dimensions by figuring out if any are all 0
548                        bool hasU, hasV, hasW;
549                        hasU = hasV = hasW = false;
550
551                        // Pull out all the UV data for this set and reorder it based on
552                        // samples, we'll need this for reference later
553                        // get Elements from uvspace Property
554            ClusterProperty uvProp(uvClusterPropertiesRefArray[i]);
555                        CClusterPropertyElementArray uvElements = uvProp.GetElements();
556
557                        // Add this to an map from uv set name to index
558                        String textureProjectionName = XSItoOgre(uvProp.GetName());
559                        mTextureProjectionMap[textureProjectionName] = i;
560
561                        // Now, each Element here is actually a CDoubleArray of the u,v,w values
562                        // However it's not in order of samplers, we need to use the Array
563                        // linked to from the Elements collection under the cluster (not the
564                        // cluster property, confusing I know) to figure out what sampler
565                        // index it is, i.e.
566                        // samplerUVs[Array[j]] = Element[j]
567                        // In all casesmCurrentSamplerSets in XSI element properties are referenced back to
568                        // their original poly index via this array, ie all cluster properties
569                        // in the same cluster are dereferenced in the same way.
570                        CLongArray derefArray = samplePointClusterUV.GetElements().GetArray();
571
572                        for (int j = 0; j < uvElements.GetCount(); ++j)
573                        {
574                                CDoubleArray curUVW(uvElements.GetItem(j));
575                                size_t samplerIndex = derefArray[j];
576                                samplerUVs[samplerIndex].x = curUVW[0];//u
577                                samplerUVs[samplerIndex].y = 1.0f - curUVW[1];//v (invert)
578                                samplerUVs[samplerIndex].z = curUVW[2];//w
579
580                                if (!hasU && curUVW[0] != 0)
581                                        hasU = true;
582                                if (!hasV && curUVW[1] != 0)
583                                        hasV = true;
584                                if (!hasW && curUVW[2] != 0)
585                                        hasW = true;
586
587                        }
588                       
589                        // save dimensions
590                        mCurrentTextureCoordDimensions.push_back(
591                                (hasU?1:0) + (hasV?1:0) + (hasW?1:0));
592
593        }
594
595
596        // do we have vertex colours?
597        ClusterProperty vertexColourClusterProperty = xsiMesh->mesh.GetCurrentVertexColor();
598        if (vertexColourClusterProperty.IsValid())
599            mCurrentHasVertexColours = true;
600                else
601                        mCurrentHasVertexColours = false;
602
603               
604                /* Create any ProtoSubMeshes which don't exist yet for the
605                 * materials in question, and define the PolygonCluster map
606                 */
607                // Main material (will never exist if not merging submeshes)
608                String materialName = mMaterialPrefix + 
609                        XSItoOgre(xsiMesh->obj.GetMaterial().GetName());
610                registerMaterial(materialName, xsiMesh->obj.GetMaterial());
611               
612                mMainProtoMesh = createOrRetrieveProtoSubMesh(
613                        materialName, 
614                        XSItoOgre(xsiMesh->obj.GetName()),
615                        mCurrentTextureCoordDimensions, 
616                        mCurrentHasVertexColours);
617               
618                // For each polygon cluster
619        CRefArray polygonClusters;
620        // Filter to 'poly' types
621        xsiMesh->mesh.GetClusters().Filter(siPolygonCluster, CStringArray(), L"", 
622                        polygonClusters);
623                mPolygonToProtoSubMeshList.clear();
624        for(i = 0; i < polygonClusters.GetCount(); ++i)
625        {
626                        Cluster cluster(polygonClusters[i]);   
627                        // Is the material different for this poly cluster?
628                        if (cluster.GetMaterial() != xsiMesh->obj.GetMaterial())
629                        {
630                                String submatName = mMaterialPrefix + 
631                                        XSItoOgre(cluster.GetMaterial().GetName());
632                                registerMaterial(submatName, cluster.GetMaterial());
633                                ProtoSubMesh* ps = createOrRetrieveProtoSubMesh(
634                                        submatName,
635                                        XSItoOgre(cluster.GetName()),
636                                        mCurrentTextureCoordDimensions, 
637                                        mCurrentHasVertexColours);
638                                // Each element is a polygon index
639                                CLongArray elems = cluster.GetElements().GetArray();
640                                for (int p = 0; p < elems.GetCount(); ++p)
641                                {
642                                        mPolygonToProtoSubMeshList[elems[p]] = ps;
643                                }
644                        }
645                }
646
647                return true;
648
649
650        }
651        //-----------------------------------------------------------------------
652        void XsiMeshExporter::postprocessPolygonMesh(PolygonMeshEntry* xsiMesh)
653        {
654                // clear all position index remaps, incase merged
655                for (MaterialProtoSubMeshMap::iterator m = mMaterialProtoSubmeshMap.begin();
656                        m != mMaterialProtoSubmeshMap.end(); ++m)
657                {
658
659                        for (ProtoSubMeshList::iterator p = m->second->begin();
660                                p != m->second->end(); ++p)
661                        {
662                                ProtoSubMesh* ps = *p;
663                                ps->posIndexRemap.clear();
664                        }
665                }
666
667                // free temp UV data now
668                for(SamplerSetList::iterator s = mCurrentSamplerSets.begin();
669                        s != mCurrentSamplerSets.end(); ++s)
670                {
671                        // init sampler points
672                        delete [] *s;
673                }
674                mCurrentSamplerSets.clear();
675                mCurrentTextureCoordDimensions.clear();
676               
677        }
678        //-----------------------------------------------------------------------
679        void XsiMeshExporter::processBoneAssignments(Mesh* pMesh, PolygonMeshEntry* xsiMesh)
680        {
681                // We have to iterate over the clusters which have envelope assignments
682                // then, for each protosubmesh which uses this polymesh, we need to create
683                // a bone assignment for each deformer, not forgetting to add one for
684                // each duplicated copy of this vertex too
685                // We build up a global list of deformers as we go which will get passed
686                // back to the top-level caller to build a skeleton from later
687                CRefArray clusterRefArray;
688                // Filter to 'vertex' types
689                xsiMesh->mesh.GetClusters().Filter(
690                        siVertexCluster,CStringArray(),L"",clusterRefArray);
691
692
693
694                for(int i = 0; i < clusterRefArray.GetCount(); ++i)
695                {
696                        Cluster cluster(clusterRefArray[i]);   
697
698                        CRefArray envelopes = cluster.GetEnvelopes();
699                        for (int e = 0; e < envelopes.GetCount(); ++e)
700                        {
701                                Envelope envelope(envelopes[e]);
702
703                                // Get mapping from cluster element index to geometry position index
704                                CLongArray derefArray = envelope.GetElements(CTime().GetTime()).GetArray();
705
706                                CRefArray deformers = envelope.GetDeformers();
707                                for (int d = 0; d < deformers.GetCount(); ++d)
708                                {
709                                        X3DObject deformer(deformers[d]);
710                                        // Has this deformer been allocated a boneID already?
711                                        String deformerName = XSItoOgre(deformer.GetName());
712                                        DeformerMap::iterator di = 
713                                                mXsiDeformerMap.find(deformerName);
714                                        DeformerEntry* deformerEntry;
715                                        bool newDeformerEntry = false;
716                                        bool atLeastOneAssignment = false;
717                                        if (di == mXsiDeformerMap.end())
718                                        {
719                                                deformerEntry = new DeformerEntry(mXsiDeformerMap.size(), deformer);
720                                                deformerEntry->hasVertexAssignments = true;
721                                                newDeformerEntry = true;
722                                        }
723                                        else
724                                        {
725                                                deformerEntry = di->second;
726                                        }
727
728                                        // Get the weights for this deformer
729                                        CDoubleArray weights = 
730                                                envelope.GetDeformerWeights(deformer, CTime().GetTime());
731                                        // Weights are in order of cluster elements, we need to dereference
732                                        // those to the original point index using the cluster element array
733                                        for (int w = 0; w < weights.GetCount(); ++w)
734                                        {
735                                                size_t positionIndex = derefArray[w];
736                                                float weight = weights[w];
737                                                // Skip zero weights
738                                                if (weight == 0.0f)
739                                                        continue;
740
741                                                // Locate ProtoSubMeshes which use this mesh
742                                                for (MaterialProtoSubMeshMap::iterator mi = mMaterialProtoSubmeshMap.begin();
743                                                        mi != mMaterialProtoSubmeshMap.end(); ++mi)
744                                                {
745                                                        for (ProtoSubMeshList::iterator psi = mi->second->begin();
746                                                                psi != mi->second->end(); ++psi)
747                                                        {
748                                                                ProtoSubMesh* ps = *psi;
749                                                                ProtoSubMesh::PolygonMeshOffsetMap::iterator poli = 
750                                                                        ps->polygonMeshOffsetMap.find(xsiMesh);
751                                                                if (poli != ps->polygonMeshOffsetMap.end())
752                                                                {
753                                                                        // adjust index based on merging
754                                                                        size_t adjIndex = positionIndex + poli->second;
755                                                                        // look up real index
756                                                                        // If it doesn't exist, it's probably on a seam
757                                                                        // between clusters and we can safely skip it
758                                                                        IndexRemap::iterator remi = ps->posIndexRemap.find(adjIndex);
759                                                                        if (remi != ps->posIndexRemap.end())
760                                                                        {
761
762                                                                                size_t vertIndex = remi->second;
763                                                                                bool moreVerts = true;
764                                                                                // add UniqueVertex and clones
765                                                                                while (moreVerts)
766                                                                                {
767                                                                                        UniqueVertex& vertex = ps->uniqueVertices[vertIndex];
768                                                                                        VertexBoneAssignment vba;
769                                                                                        vba.boneIndex = deformerEntry->boneID;
770                                                                                        vba.vertexIndex = vertIndex;
771                                                                                        vba.weight = weight;
772                                                                                        ps->boneAssignments.insert(
773                                                                                                Mesh::VertexBoneAssignmentList::value_type(vertIndex, vba));
774                                                                                        atLeastOneAssignment = true;
775
776                                                                                        if (vertex.nextIndex == 0)
777                                                                                        {
778                                                                                                moreVerts = false;
779                                                                                        }
780                                                                                        else
781                                                                                        {
782                                                                                                vertIndex = vertex.nextIndex;
783                                                                                        }
784                                                                                }
785                                                                        }
786
787                                                                }
788                                                        }
789
790                                                }
791
792
793
794                                        }
795
796                                        // Only add new deformer if we actually had any assignments
797                                        if (newDeformerEntry && atLeastOneAssignment)
798                                        {
799                                                mXsiDeformerMap[deformerName] = deformerEntry;
800                                        }
801
802
803
804                                       
805                                }
806                               
807
808                        }
809                }
810
811
812        }
813        //-----------------------------------------------------------------------
814        void XsiMeshExporter::processShapeKeys(Mesh* pMesh, PolygonMeshEntry* xsiMesh)
815        {
816                // ShapeKeys are kept underneath clusters
817                // The clusters are point clusters, not poly clusters like those used
818                // to define materials
819                // Each point cluster identifies the list of points involved, and the shape key
820                // contains the offsets for that key
821                // Like bone assignments, we have to ensure we add keys for duplicated points
822
823                // Get points & vertices incase we need to convert from local reference frame
824                CPointRefArray pointsArray = xsiMesh->mesh.GetPoints();
825                CVertexRefArray verticesArray = xsiMesh->mesh.GetVertices();
826
827        CRefArray clusterRefArray;
828        // Filter to 'pnt' types
829        xsiMesh->mesh.GetClusters().Filter(
830                        siVertexCluster,CStringArray(),L"",clusterRefArray);
831
832                for(int i = 0; i < clusterRefArray.GetCount(); ++i)
833                {
834                        Cluster cluster(clusterRefArray[i]);
835
836                        // Cluster elements are the vertex indices affected
837                        CClusterElementArray vertexIndexArray = cluster.GetElements();
838
839                        CRefArray clusterProperties = cluster.GetProperties();
840                        for (int p = 0; p < clusterProperties.GetCount(); ++p)
841                        {
842                                ClusterProperty prop(clusterProperties[p]);
843                                // Pull out only shape keys
844                                if (prop.GetPropertyType() == siClusterPropertyShapeKeyType)
845                                {
846                                        ShapeKey shapeKey(prop);
847
848                                        Parameter keyTypeParam = shapeKey.GetParameter(L"KeyType");
849                                        CValue currMode = keyTypeParam.GetValue();
850                                        /*
851                                        StringUtil::StrStreamType str;
852                                        str << "KeyType = " << (unsigned short)currMode <<
853                                                " siShapeLocalReferenceMode = " << (unsigned short)siShapeLocalReferenceMode <<
854                                                " siShapeAbsoluteReferenceMode = " << (unsigned short)siShapeAbsoluteReferenceMode <<
855                                                " siShapeObjectReferenceMode = " << (unsigned short)siShapeObjectReferenceMode;
856                                        LogOgreAndXSI(str.str());
857                                        */
858
859                                        // XSI bug? siShapeReferenceMode enum doesn't match runtime values
860                                        // Local = 1, Absolute = 0, Object = 2 in real life
861                                        // Logged with Softimage as UDEV00203965
862                                        bool isLocalSpace = 
863                                                ((unsigned short)currMode) == 1; //siShapeLocalReferenceMode;
864                                        bool isGlobalSpace = 
865                                                ((unsigned short)currMode) == 0;//siShapeAbsoluteReferenceMode;
866
867                                        LogOgreAndXSI("Found shape key " + XSItoOgre(shapeKey.GetName()));
868                                        // elements of property are the offsets, a double array of values
869                                        CClusterPropertyElementArray shapeElements = shapeKey.GetElements();
870
871                                        // Locate ProtoSubMeshes which use this mesh
872                                        for (MaterialProtoSubMeshMap::iterator mi = mMaterialProtoSubmeshMap.begin();
873                                                mi != mMaterialProtoSubmeshMap.end(); ++mi)
874                                        {
875                                                for (ProtoSubMeshList::iterator psi = mi->second->begin();
876                                                        psi != mi->second->end(); ++psi)
877                                                {
878                                                        ProtoSubMesh* ps = *psi;
879                                                        ProtoSubMesh::PolygonMeshOffsetMap::iterator poli = 
880                                                                ps->polygonMeshOffsetMap.find(xsiMesh);
881                                                        if (poli != ps->polygonMeshOffsetMap.end())
882                                                        {
883                                                                // remap from mesh vertex index to proto vertex index
884                                                                size_t indexAdjustment = poli->second;
885
886                                                                // Create a new pose, target is implied by proto, final
887                                                                // index to be determined later including merging
888                                                                Pose pose(0, XSItoOgre(shapeKey.GetName()));
889
890                                                                // Iterate per vertex affected
891                                                                for (int xi = 0; xi < vertexIndexArray.GetCount(); ++xi)
892                                                                {
893                                                                        // Index
894                                                                        size_t positionIndex = vertexIndexArray.GetItem(xi);
895                                                                        // Now get offset
896                                                                        CDoubleArray xsiOffset = shapeElements.GetItem(xi);
897                                                                        Vector3 offset(xsiOffset[0], xsiOffset[1], xsiOffset[2]);
898
899                                                                        // Skip zero offsets
900                                                                        if (offset == Vector3::ZERO)
901                                                                                continue;
902
903
904                                                                        if (isLocalSpace)
905                                                                        {
906                                                                                // Local reference mode -> object space
907                                                                                // Local mode is the most popular since in XSI
908                                                                                // it plays nice with skeletal animation, but
909                                                                                // it's relative to the _point's_ local space
910
911                                                                                // Get local axes
912                                                                                // XSI defines local space as:
913                                                                                // Y = vertex normal
914                                                                                // X = normalised projection of first edge
915                                                                                //     from vertex onto normal plane
916                                                                                // Z = cross product of above
917                                                                                Point point(pointsArray[positionIndex]);
918                                                                                bool normalValid;
919                                                                                Vector3 localY = XSItoOgre(point.GetNormal(normalValid));
920                                                                                Vertex vertex(verticesArray.GetItem(positionIndex));
921                                                                                CEdgeRefArray edgeArray = vertex.GetNeighborEdges();
922                                                                                if (normalValid && edgeArray.GetCount() > 0)
923                                                                                {
924
925                                                                                        Edge edge(edgeArray[0]);
926                                                                                        CVertexRefArray verticesOnEdge = edge.GetNeighborVertices();
927                                                                                        Vertex otherVertex = 
928                                                                                                (verticesOnEdge[0] == vertex) ?
929                                                                                                verticesOnEdge[1] : verticesOnEdge[0];
930                                                                                        Vector3 edgeVector
931                                                                                                = XSItoOgre(otherVertex.GetPosition())
932                                                                                                        - XSItoOgre(vertex.GetPosition());
933                                                                                        // Project the vector onto the normal plane (d irrelevant)
934                                                                                        Plane normPlane(localY, 0);
935                                                                                        Vector3 localX = normPlane.projectVector(edgeVector);
936                                                                                        localX.normalise();
937
938                                                                                        Vector3 localZ = localX.crossProduct(localY);
939
940                                                                                        // multiply out position by local axes to form
941                                                                                        // final position
942                                                                                        offset = (localX * offset.x) + 
943                                                                                                (localY * offset.y) + 
944                                                                                                (localZ * offset.z);
945
946                                                                                }
947
948                                                                        }
949                                                                       
950                                                                        if (!isGlobalSpace)
951                                                                        {
952                                                                                // shape is in object space, if object is parented
953                                                                                // by a null or a static bone, we need to adjust the
954                                                                                // shape offset since this inherited transform is
955                                                                                // baked into the base OGRE mesh (to preserve
956                                                                                // relative positioning of parts)
957                                                                                // This deals with rotation and scaling
958
959                                                                                // If object is parented
960                                                                                // Don't know if anyone really uses this
961                                                                                // Convert global to object space
962                                                                                MATH::CTransformation xform = 
963                                                                                        xsiMesh->obj.GetKinematics().GetGlobal().GetTransform();
964                                                                                MATH::CVector3 off(offset.x, offset.y, offset.z);
965                                                                                off.MulByTransformationInPlace(xform);
966                                                                                // now adjust for position since OGRE's poses
967                                                                                // are relative to original vertex position
968                                                                                off -= xform.GetTranslation();
969
970                                                                                offset = XSItoOgre(off);
971
972
973
974                                                                        }
975
976
977                                                                        // adjust index based on merging
978                                                                        size_t adjIndex = positionIndex + indexAdjustment;
979                                                                        // look up real index
980                                                                        // If it doesn't exist, it's probably on a seam
981                                                                        // between clusters and we can safely skip it
982                                                                        IndexRemap::iterator remi = ps->posIndexRemap.find(adjIndex);
983                                                                        if (remi != ps->posIndexRemap.end())
984                                                                        {
985
986                                                                                size_t vertIndex = remi->second;
987                                                                                bool moreVerts = true;
988
989
990                                                                                // add UniqueVertex and clones
991                                                                                while (moreVerts)
992                                                                                {
993                                                                                        UniqueVertex& vertex = ps->uniqueVertices[vertIndex];
994
995                                                                                        // Create a vertex pose entry
996                                                                                        pose.addVertex(vertIndex, offset);
997
998                                                                                        if (vertex.nextIndex == 0)
999                                                                                        {
1000                                                                                                moreVerts = false;
1001                                                                                        }
1002                                                                                        else
1003                                                                                        {
1004                                                                                                vertIndex = vertex.nextIndex;
1005                                                                                        }
1006                                                                                } // more duplicate verts
1007                                                                        } // found remap?
1008                                                                } // for each vertex affected
1009
1010                                                                // Add pose to proto
1011                                                                ps->poseList.push_back(pose);
1012
1013                                                                // record that we used this shape key
1014                                                                ps->shapeKeys.Add(shapeKey);
1015
1016
1017                                                        } // proto found?
1018                                                }// for each proto
1019                                        } // for each material/protolist
1020                                } // shape key cluster property?
1021                        } // for each cluster property
1022                } // for each cluster
1023
1024        }
1025        //-----------------------------------------------------------------------
1026        void XsiMeshExporter::buildShapeClipList(ShapeClipList& listToPopulate)
1027        {
1028                // Process all mixers
1029                Model root = mXsiApp.GetActiveSceneRoot();
1030                if (root.HasMixer())
1031                        buildShapeClipList(root.GetMixer(), listToPopulate);
1032
1033                // Get all child models (recursive)
1034                CRefArray models = root.GetModels();
1035                for (int m = 0; m < models.GetCount(); ++m)
1036                {
1037                        Model model(models[m]);
1038                        if (model.HasMixer())
1039                                buildShapeClipList(model.GetMixer(), listToPopulate);
1040                }
1041
1042        }
1043        //-----------------------------------------------------------------------
1044        void XsiMeshExporter::buildShapeClipList(ClipContainer& container, 
1045                ShapeClipList& listToPopulate)
1046        {
1047                CRefArray clips = container.GetClips();
1048                for (int c = 0; c < clips.GetCount(); ++c)
1049                {
1050                        if (clips[c].IsA(siClipContainerID))
1051                        {
1052                                ClipContainer container(clips[c]);
1053                                // cascade
1054                                buildShapeClipList(container, listToPopulate);
1055                        }
1056                        else
1057                        {
1058
1059                                XSI::Clip clip(clips[c]);
1060                                XSI::CString clipType = clip.GetType();
1061                                if (clip.GetType() == siClipShapeType)
1062                                {
1063                                        XSI::TimeControl timeControl = clip.GetTimeControl();
1064                                        ShapeClipEntry sce;
1065                                        sce.clip = clip;
1066                                        sce.startFrame = sce.originalStartFrame = timeControl.GetStartOffset();
1067                                        long length = (1.0 / timeControl.GetScale()) * 
1068                                                (timeControl.GetClipOut() - timeControl.GetClipIn() + 1);
1069                                        sce.endFrame = sce.startFrame + length - 1;
1070
1071                                        // Find link to shape
1072                                        sce.keytoPose = 0;
1073                                        ActionSource source(clip.GetSource());
1074                                        CRefArray sourceItems = source.GetItems();
1075                                        assert (sourceItems.GetCount() == 1 && "More than one source item on shape clip!");
1076                                        AnimationSourceItem sourceItem(sourceItems[0]);
1077                                        // Source is the shape key
1078                                        // Locate this in the list we built while building poses
1079                                        for (ShapeKeyMapping::iterator skm = mShapeKeyMapping.begin();
1080                                                skm != mShapeKeyMapping.end(); ++skm)
1081                                        {
1082                                                ShapeKeyToPoseEntry& mapping = *skm;
1083                                                if(mapping.shapeKey == sourceItem.GetSource())
1084                                                {
1085                                                        // bingo
1086                                                        sce.keytoPose = &(*skm);
1087                                                }
1088                                        }
1089
1090                                        listToPopulate.push_back(sce);
1091
1092                                }
1093                        }
1094
1095                }
1096
1097
1098        }
1099        //-----------------------------------------------------------------------
1100        void XsiMeshExporter::exportAnimations(Mesh* pMesh, AnimationList& animList, 
1101                Real fps)
1102        {
1103                ShapeClipList clipList;
1104                buildShapeClipList(clipList);
1105                // Add all animations
1106                for (AnimationList::iterator i = animList.begin(); i != animList.end(); ++i)
1107                {
1108                        // For each animation, we want to search for all shape clips using a
1109                        // shape key which has been used
1110                        AnimationEntry& animEntry = *i;
1111                        Animation* anim = 0;
1112
1113                        // Iterate per submesh since we want to collect a track per submesh
1114                        // Iterate over the 'target index' which is submesh index + 1
1115                        for (ushort targetIndex = 1; targetIndex <= pMesh->getNumSubMeshes(); ++targetIndex)
1116                        {
1117                                // find all shape clips for this submesh overlapping this time period
1118                                // we'll clamp the clip too
1119                                ShapeClipList submeshClipList;
1120                                std::set<long> keyframeList;
1121
1122                                deriveShapeClipAndKeyframeList(targetIndex, animEntry, clipList, 
1123                                        submeshClipList, keyframeList);
1124
1125                                if (!submeshClipList.empty())
1126                                {
1127
1128                                        // Create animation if we haven't already
1129                                        if (!anim)
1130                                        {
1131                                                Real len = (float)(animEntry.endFrame - animEntry.startFrame + 1) / fps;
1132                                                anim = pMesh->createAnimation(animEntry.animationName, len);
1133                                        }
1134                                       
1135                                        // Create track
1136                                        VertexAnimationTrack* track = 
1137                                                anim->createVertexTrack(targetIndex, VAT_POSE);
1138                                       
1139                                        // Now sample all the clips on all the keyframes
1140                                        for (std::set<long>::iterator k = keyframeList.begin();
1141                                                k != keyframeList.end(); ++k)
1142                                        {
1143                                                // Create a key
1144                                                long frameNum = *k;
1145                                                Real keyTime = (float)(frameNum - animEntry.startFrame) / fps;
1146                                                VertexPoseKeyFrame* keyFrame = 
1147                                                        track->createVertexPoseKeyFrame(keyTime);
1148
1149                                                // Sample all the clips
1150                                                for (ShapeClipList::iterator c = submeshClipList.begin();
1151                                                        c != submeshClipList.end(); ++c)
1152                                                {
1153                                                        ShapeClipEntry& sce = *c;
1154                                                        if(frameNum >= sce.startFrame && 
1155                                                                frameNum <= sce.endFrame)
1156                                                        {
1157
1158                                                                // map the keyframe number to a local number
1159                                                                long localFrameNum = frameNum - sce.originalStartFrame *
1160                                                                        sce.clip.GetTimeControl().GetScale();
1161
1162                                                                // sample pose influences
1163                                                                // Iterate over the animated parameters, find a 'weight' fcurve entry
1164                                                                bool fcurveFound = false;
1165                                                                CRefArray params = sce.clip.GetAnimatedParameters();
1166                                                                for (int p = 0; p < params.GetCount(); ++p)
1167                                                                {
1168                                                                        Parameter param(params[p]);
1169                                                                        if (param.GetSource().IsA(siFCurveID))
1170                                                                        {
1171                                                                                fcurveFound = true;
1172                                                                                FCurve fcurve(param.GetSource());
1173                                                                                CValue val = fcurve.Eval(localFrameNum);
1174                                                                                if ((float)val != 0.0f)
1175                                                                                {
1176                                                                                        keyFrame->addPoseReference(
1177                                                                                                sce.keytoPose->poseIndex, val);
1178                                                                                }
1179                                                                               
1180                                                                        }
1181                                                                }
1182
1183                                                                if (!fcurveFound)
1184                                                                {
1185                                                                        // No fcurves, so just assume 1.0 weight since that's
1186                                                                        // how XSI deals with it
1187                                                                        keyFrame->addPoseReference(sce.keytoPose->poseIndex, 1.0f);
1188
1189                                                                }
1190                                                        }
1191                                                }
1192                                        }
1193                                       
1194                                }
1195
1196                        }
1197                }
1198
1199        }
1200        //-----------------------------------------------------------------------
1201        void XsiMeshExporter::deriveShapeClipAndKeyframeList(ushort targetIndex, 
1202                AnimationEntry& animEntry, ShapeClipList& inClipList, 
1203                ShapeClipList& outClipList, std::set<long>& keyFrameList)
1204        {
1205                for (ShapeClipList::iterator sci = inClipList.begin(); 
1206                        sci != inClipList.end(); ++sci)
1207                {
1208                        ShapeClipEntry& sce = *sci;
1209
1210                        if (sce.startFrame <= animEntry.endFrame &&
1211                                sce.endFrame >= animEntry.startFrame && 
1212                                sce.keytoPose->targetHandle == targetIndex)
1213                        {
1214                                // Clip overlaps with the animation sampling area and
1215                                // applies to this submesh
1216                                ShapeClipEntry newClipEntry;
1217                                newClipEntry.originalStartFrame = sce.startFrame;
1218                                newClipEntry.startFrame = std::max(sce.startFrame, animEntry.startFrame);
1219                                newClipEntry.endFrame = std::min(sce.endFrame, animEntry.endFrame);
1220                                newClipEntry.clip = sce.clip;
1221                                newClipEntry.keytoPose = sce.keytoPose;
1222                                outClipList.push_back(newClipEntry);
1223
1224                                // Iterate over the animated parameters, find a 'weight' fcurve entry
1225                                CRefArray params = sce.clip.GetAnimatedParameters();
1226                                for (int p = 0; p < params.GetCount(); ++p)
1227                                {
1228                                        Parameter param(params[p]);
1229                                        if (param.GetSource().IsA(siFCurveID))
1230                                        {
1231                                                FCurve fcurve(param.GetSource());
1232                                                CRefArray keys = fcurve.GetKeys();
1233                                                for (int k = 0; k < keys.GetCount(); ++k)
1234                                                {
1235                                                        FCurveKey key(keys[k]);
1236                                                        // convert from key time to global frame number
1237                                                        CTime time = key.GetTime();
1238                                                        long frameNum = (long)((double)(time.GetTime() + sce.startFrame)
1239                                                                / sce.clip.GetTimeControl().GetScale());
1240                                                        keyFrameList.insert(frameNum);
1241                                                }
1242                               
1243
1244                                        }
1245                                }
1246
1247                        }
1248                }
1249                // Always sample start & end frames
1250                keyFrameList.insert(animEntry.startFrame);
1251                keyFrameList.insert(animEntry.endFrame);
1252
1253        }
1254        //-----------------------------------------------------------------------
1255        void XsiMeshExporter::exportProtoSubMeshes(Mesh* pMesh)
1256        {
1257                // Take the list of ProtoSubMesh instances and bake a SubMesh per
1258                // instance, then clear the list
1259
1260                for (MaterialProtoSubMeshMap::iterator mi = mMaterialProtoSubmeshMap.begin();
1261                        mi != mMaterialProtoSubmeshMap.end(); ++mi)
1262                {
1263                        for (ProtoSubMeshList::iterator psi = mi->second->begin();
1264                                psi != mi->second->end(); ++psi)
1265                        {
1266                                // export each one
1267                                exportProtoSubMesh(pMesh, *psi);
1268
1269                                // free it
1270                                delete *psi;
1271                        }
1272                        // delete proto list
1273                        delete mi->second;
1274                }
1275                mMaterialProtoSubmeshMap.clear();
1276        }
1277        //-----------------------------------------------------------------------
1278        void XsiMeshExporter::exportProtoSubMesh(Mesh* pMesh, ProtoSubMesh* proto)
1279        {
1280                // Skip protos which have ended up empty
1281                if (proto->indices.empty())
1282                        return;
1283
1284        SubMesh* sm = 0;
1285        if (proto->name.empty())
1286        {
1287            // anonymous submesh
1288            sm = pMesh->createSubMesh();
1289        }
1290        else
1291        {
1292            // named submesh
1293            sm = pMesh->createSubMesh(proto->name);
1294        }
1295
1296        // Set material
1297        sm->setMaterialName(proto->materialName);
1298        // never use shared geometry
1299        sm->useSharedVertices = false;
1300        sm->vertexData = new VertexData();
1301        // always do triangle list
1302        sm->indexData->indexCount = proto->indices.size();
1303               
1304                sm->vertexData->vertexCount = proto->uniqueVertices.size();
1305        // Determine index size
1306        bool use32BitIndexes = false;
1307        if (proto->uniqueVertices.size() > 65536)
1308        {
1309            use32BitIndexes = true;
1310        }
1311
1312        sm->indexData->indexBuffer = 
1313            HardwareBufferManager::getSingleton().createIndexBuffer(
1314            use32BitIndexes ? HardwareIndexBuffer::IT_32BIT : HardwareIndexBuffer::IT_16BIT,
1315            sm->indexData->indexCount,
1316            HardwareBuffer::HBU_STATIC_WRITE_ONLY);
1317        if (use32BitIndexes)
1318        {
1319            uint32* pIdx = static_cast<uint32*>(
1320                sm->indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
1321            writeIndexes(pIdx, proto->indices);
1322            sm->indexData->indexBuffer->unlock();
1323        }
1324        else
1325        {
1326            uint16* pIdx = static_cast<uint16*>(
1327                sm->indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
1328            writeIndexes(pIdx, proto->indices);
1329            sm->indexData->indexBuffer->unlock();
1330        }
1331
1332
1333        // define vertex declaration
1334        unsigned buf = 0;
1335        size_t offset = 0;
1336                // always add position and normal
1337        sm->vertexData->vertexDeclaration->addElement(buf, offset, VET_FLOAT3, VES_POSITION);
1338        offset += VertexElement::getTypeSize(VET_FLOAT3);
1339                // Split vertex data after position if poses present
1340                if (!proto->poseList.empty())
1341                {
1342                        buf++;
1343                        offset = 0;
1344                }
1345        sm->vertexData->vertexDeclaration->addElement(buf, offset, VET_FLOAT3, VES_NORMAL);
1346        offset += VertexElement::getTypeSize(VET_FLOAT3);
1347        // split vertex data here if animated
1348        if (pMesh->hasSkeleton())
1349        {
1350            buf++;
1351            offset = 0;
1352        }
1353                // Optional vertex colour
1354        if(proto->hasVertexColours)
1355        {
1356            sm->vertexData->vertexDeclaration->addElement(buf, offset, VET_COLOUR, VES_DIFFUSE);
1357            offset += VertexElement::getTypeSize(VET_COLOUR);
1358        }
1359        // Define UVs
1360        for (unsigned short uvi = 0; uvi < proto->textureCoordDimensions.size(); ++uvi)
1361        {
1362                        VertexElementType uvType = 
1363                                VertexElement::multiplyTypeCount(
1364                                        VET_FLOAT1, proto->textureCoordDimensions[uvi]);
1365            sm->vertexData->vertexDeclaration->addElement(
1366                                buf, offset, uvType, VES_TEXTURE_COORDINATES, uvi);
1367            offset += VertexElement::getTypeSize(uvType);
1368        }
1369
1370        // create & fill buffer(s)
1371        for (unsigned short b = 0; b <= sm->vertexData->vertexDeclaration->getMaxSource(); ++b)
1372        {
1373            createVertexBuffer(sm->vertexData, b, proto->uniqueVertices);
1374        }
1375
1376                // deal with any bone assignments
1377                if (!proto->boneAssignments.empty())
1378                {
1379                        // rationalise first (normalises and strips out any excessive bones)
1380                        sm->parent->_rationaliseBoneAssignments(
1381                                sm->vertexData->vertexCount, proto->boneAssignments);
1382
1383                        for (Mesh::VertexBoneAssignmentList::iterator bi = proto->boneAssignments.begin();
1384                                bi != proto->boneAssignments.end(); ++bi)
1385                        {
1386                                sm->addBoneAssignment(bi->second);
1387                        }
1388                }
1389
1390                // poses
1391                // derive target index (current submesh index + 1 since 0 is shared geom)
1392                ushort targetIndex = pMesh->getNumSubMeshes(); 
1393                ushort sk = 0;
1394                for (std::list<Pose>::iterator pi = proto->poseList.begin();
1395                        pi != proto->poseList.end(); ++pi, ++sk)
1396                {
1397                        Pose* pose = pMesh->createPose(targetIndex, pi->getName());
1398                        Pose::VertexOffsetIterator vertIt = 
1399                                pi->getVertexOffsetIterator();
1400                        while (vertIt.hasMoreElements())
1401                        {
1402                                pose->addVertex(vertIt.peekNextKey(), vertIt.peekNextValue());
1403                                vertIt.getNext();
1404                        }
1405
1406                        // record shape key to pose mapping for animation later
1407                        ShapeKeyToPoseEntry se;
1408                        se.shapeKey = proto->shapeKeys[sk];
1409                        se.poseIndex = pMesh->getPoseCount() - 1;
1410                        se.targetHandle = targetIndex;
1411                        mShapeKeyMapping.push_back(se);
1412
1413                }
1414
1415        }
1416        //-----------------------------------------------------------------------
1417        void XsiMeshExporter::buildPolygonMeshList(bool includeChildren)
1418        {
1419                Selection sel(mXsiApp.GetSelection());
1420                if (sel.GetCount() == 0)
1421                {
1422                        // Whole scene
1423                        // Derive the scene root
1424                        X3DObject sceneRoot(mXsiApp.GetActiveSceneRoot());
1425                        findPolygonMeshes(sceneRoot, true);
1426                }
1427                else
1428                {
1429                        // iterate over selection
1430                        for (int i = 0; i < sel.GetCount(); ++i)
1431                        {
1432                                X3DObject obj(sel[i]);
1433                                findPolygonMeshes(obj,includeChildren);
1434                        }
1435                }
1436        }
1437        //-----------------------------------------------------------------------
1438        void XsiMeshExporter::findPolygonMeshes(X3DObject& x3dObj, bool recurse)
1439        {
1440                // Check validity of current object
1441                if (!x3dObj.IsValid())
1442                {
1443                        OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, 
1444                                "Invalid X3DObject found",
1445                                "XsiMeshExporter::exportX3DObject");
1446                }
1447                // Log a message in script window
1448                CString name = x3dObj.GetName() ;
1449                LogOgreAndXSI(L"-- Traversing " +  name) ;
1450
1451
1452                // locate any geometry
1453                if (!x3dObj.IsA(siCameraID) && 
1454                        !x3dObj.IsA(siLightID) && 
1455                        !x3dObj.IsA(siNullID) && 
1456                        !x3dObj.IsA(siModelID))
1457                {
1458                        Primitive prim(x3dObj.GetActivePrimitive());
1459                        if (prim.IsValid())
1460                        {
1461                                Geometry geom(prim.GetGeometry());
1462                                if (geom.GetRef().GetClassID() == siPolygonMeshID)
1463                                {
1464                                        // add it to the list
1465                                        PolygonMesh pmesh(geom);
1466                                        mXsiPolygonMeshList.insert(
1467                                                new PolygonMeshEntry(pmesh, x3dObj));
1468
1469                                        LogOgreAndXSI(L"-- Queueing " +  name) ;
1470                                }
1471                        }
1472
1473                }
1474
1475                // Cascade into children
1476                if (recurse)
1477                {
1478                        CRefArray children = x3dObj.GetChildren();
1479
1480                        for(long i = 0; i < children.GetCount(); i++)
1481                        {
1482                                X3DObject childObj = children[i];
1483                                findPolygonMeshes(childObj, recurse);
1484                        }
1485                }
1486
1487        }
1488    //-----------------------------------------------------------------------
1489        void XsiMeshExporter::cleanupPolygonMeshList(void)
1490        {
1491                for (PolygonMeshList::iterator pm = mXsiPolygonMeshList.begin();
1492                        pm != mXsiPolygonMeshList.end(); ++pm)
1493                {
1494                        delete *pm;
1495                }
1496                mXsiPolygonMeshList.clear();
1497        }
1498        //-----------------------------------------------------------------------
1499        void XsiMeshExporter::cleanupDeformerMap(void)
1500        {
1501                for (DeformerMap::iterator d = mXsiDeformerMap.begin();
1502                        d != mXsiDeformerMap.end(); ++d)
1503                {
1504                        delete d->second;
1505                }
1506                mXsiDeformerMap.clear();
1507        }
1508        //-----------------------------------------------------------------------
1509        void XsiMeshExporter::cleanupMaterialMap(void)
1510        {
1511                for (MaterialMap::iterator d = mXsiMaterialMap.begin();
1512                        d != mXsiMaterialMap.end(); ++d)
1513                {
1514                        delete d->second;
1515                }
1516                mXsiMaterialMap.clear();
1517        }
1518        //-----------------------------------------------------------------------
1519        void XsiMeshExporter::deriveSamplerIndices(const Triangle& tri, 
1520                const PolygonFace& face, size_t* samplerIndices)
1521        {
1522                //We want to find what is the SampleIndex associated with 3
1523                //vertex in a Triangle
1524                CPointRefArray facePoints = face.GetPoints();
1525
1526                //Get the position of the 3 vertex in the triangle
1527                MATH::CVector3Array triPos = tri.GetPositionArray();
1528
1529                //Get the position of the N Points in the polygon
1530                MATH::CVector3Array facePos = facePoints.GetPositionArray();
1531
1532                //To know if the 3 vertex have a point in the same position
1533                bool found[3];
1534                found[0] = false;
1535                found[1] = false;
1536                found[2] = false;
1537
1538                int p,t;
1539                //For the 3 triangle vertices
1540                for(t=0; t<3 ; t++)
1541                {       //for each polygon point
1542                        for(p=0; p<facePos.GetCount() && !found[t]; p++)
1543                        {
1544                                //Check if the position is the same
1545                                if(triPos[t] == facePos[p])
1546                                {
1547                                        //if so, we know the PolygonPointIndex of the TriangleVertex
1548                                        //then, we must find what is the sample index associated
1549                                        //with this Point
1550                                        samplerIndices[t] = 
1551                                                getSamplerIndex(Facet(face), facePoints[p]);
1552                                        found[t] = true;
1553                                }
1554                        }
1555
1556                }
1557
1558                if (!found[0] || !found[1] || !found[2] )
1559                {
1560                        // Problem!
1561                        LogOgreAndXSI(L"!! Couldn't find a matching UV point!");
1562                }
1563
1564        }
1565        //-----------------------------------------------------------------------
1566        size_t XsiMeshExporter::getSamplerIndex(const Facet &f, const Point &p)
1567        {
1568                //This function check if a Sample is shared by a Facet and a Point
1569                //just by using the operator=
1570                //Only one Sample can be shared.
1571
1572                Sample curFacetSample;
1573                CSampleRefArray facetSamples( f.GetSamples() );
1574                CSampleRefArray pointSamples( p.GetSamples() );
1575
1576                for(int i = 0; i < facetSamples.GetCount(); i++ )
1577                {
1578
1579                        curFacetSample = Sample( facetSamples[i] );
1580
1581                        for(int j = 0; j < pointSamples.GetCount(); j++)
1582                        {
1583                                if(curFacetSample == Sample(pointSamples[j]))
1584                                {
1585                                        return curFacetSample.GetIndex();
1586                                }
1587                        }
1588                }
1589                // Problem!
1590                mXsiApp.LogMessage(L"!! Couldn't find a matching sample point!");
1591                return 0;
1592        }
1593    //-----------------------------------------------------------------------
1594    template <typename T> 
1595    void XsiMeshExporter::writeIndexes(T* buf, IndexList& indexes)
1596    {
1597        IndexList::const_iterator i, iend;
1598        iend = indexes.end();
1599        for (i = indexes.begin(); i != iend; ++i)
1600        {
1601            *buf++ = static_cast<T>(*i);
1602        }
1603    }
1604    //-----------------------------------------------------------------------
1605    void XsiMeshExporter::createVertexBuffer(VertexData* vd, 
1606                unsigned short bufIdx, UniqueVertexList& uniqueVertexList)
1607    {
1608        HardwareVertexBufferSharedPtr vbuf = 
1609                        HardwareBufferManager::getSingleton().createVertexBuffer(
1610                vd->vertexDeclaration->getVertexSize(bufIdx),
1611                vd->vertexCount, 
1612                HardwareBuffer::HBU_STATIC_WRITE_ONLY);
1613        vd->vertexBufferBinding->setBinding(bufIdx, vbuf);
1614        size_t vertexSize = vd->vertexDeclaration->getVertexSize(bufIdx);
1615
1616        char* pBase = static_cast<char*>(
1617                                vbuf->lock(HardwareBuffer::HBL_DISCARD));
1618
1619        VertexDeclaration::VertexElementList elems = 
1620                        vd->vertexDeclaration->findElementsBySource(bufIdx);
1621        VertexDeclaration::VertexElementList::iterator ei, eiend;
1622        eiend = elems.end();
1623        float* pFloat;
1624        RGBA* pRGBA;
1625
1626        UniqueVertexList::iterator srci = uniqueVertexList.begin();
1627
1628        for (size_t v = 0; v < vd->vertexCount; ++v, ++srci)
1629        {
1630            for (ei = elems.begin(); ei != eiend; ++ei)
1631            {
1632                VertexElement& elem = *ei;
1633                switch(elem.getSemantic())
1634                {
1635                case VES_POSITION:
1636                    elem.baseVertexPointerToElement(pBase, &pFloat);
1637                    *pFloat++ = srci->position.x;
1638                    *pFloat++ = srci->position.y;
1639                    *pFloat++ = srci->position.z;
1640                    break;
1641                case VES_NORMAL:
1642                    elem.baseVertexPointerToElement(pBase, &pFloat);
1643                    *pFloat++ = srci->normal.x;
1644                    *pFloat++ = srci->normal.y;
1645                    *pFloat++ = srci->normal.z;
1646                    break;
1647                case VES_DIFFUSE:
1648                    elem.baseVertexPointerToElement(pBase, &pRGBA);
1649                    *pRGBA = srci->colour;
1650                    break;
1651                case VES_TEXTURE_COORDINATES:
1652                    elem.baseVertexPointerToElement(pBase, &pFloat);
1653                                        for (int t = 0; t < VertexElement::getTypeCount(elem.getType()); ++t)
1654                                        {
1655                                                Real val = srci->uv[elem.getIndex()][t];
1656                                                *pFloat++ = val;
1657                                        }
1658                    break;
1659                }
1660            }
1661            pBase += vertexSize;
1662        }
1663        vbuf->unlock();
1664
1665    }
1666    //-----------------------------------------------------------------------
1667    size_t XsiMeshExporter::createOrRetrieveUniqueVertex(
1668                ProtoSubMesh* proto, size_t positionIndex, 
1669                bool positionIndexIsOriginal, const UniqueVertex& vertex)
1670    {
1671                size_t lookupIndex;
1672                if (positionIndexIsOriginal)
1673                {
1674                        // look up the original index
1675                        IndexRemap::iterator remapi = 
1676                                proto->posIndexRemap.find(positionIndex);
1677                        if (remapi == proto->posIndexRemap.end())
1678                        {
1679                                // not found, add
1680                                size_t realIndex = proto->uniqueVertices.size();
1681                                // add remap entry so we can find this again
1682                                proto->posIndexRemap[positionIndex] = realIndex;
1683                                proto->uniqueVertices.push_back(vertex);
1684                                return realIndex;
1685                        }
1686                        else
1687                        {
1688                                // Found existing mapping
1689                                lookupIndex = remapi->second;
1690                        }
1691                }
1692                else
1693                {
1694                        // Not an original index, index is real
1695                        lookupIndex = positionIndex;
1696                }
1697
1698                // If we get here, either the position isn't an original index (ie
1699                // we've already found that it doesn't match, and have cascaded)
1700                // or there is an existing entry
1701                // Get existing
1702            UniqueVertex& orig = proto->uniqueVertices[lookupIndex];
1703                // Compare, do we have the same details?
1704                if (orig == vertex)
1705                {
1706                        // ok, they match
1707                        return lookupIndex;
1708                }
1709                else
1710                {
1711            // no match, go to next or create new
1712                if (orig.nextIndex)
1713                {
1714                // cascade to the next candidate (which is a real index, not an original)
1715                return createOrRetrieveUniqueVertex(
1716                                                proto, orig.nextIndex, false, vertex);
1717            }
1718                        else
1719                        {
1720                                // No more cascades to check, must be a new one
1721                    // get new index
1722                size_t realIndex = proto->uniqueVertices.size();
1723                    orig.nextIndex = realIndex;
1724                // create new (NB invalidates 'orig' reference)
1725                proto->uniqueVertices.push_back(vertex);
1726                                // note, don't add to remap, that's only for finding the
1727                                // first entry, nextIndex is used to chain to the others
1728
1729                return realIndex;
1730                        }
1731                }
1732    }
1733    //-----------------------------------------------------------------------
1734        void XsiMeshExporter::registerMaterial(const String& name, 
1735                XSI::Material mat)
1736        {
1737                // Check we have a real-time shader based material first
1738                XSI::Parameter rtParam = mat.GetParameter(L"RealTime");
1739               
1740                if(rtParam.GetSource().IsValid() && 
1741                        rtParam.GetSource().IsA(XSI::siShaderID))
1742                {
1743                        MaterialMap::iterator i = mXsiMaterialMap.find(name);
1744                        if (i == mXsiMaterialMap.end())
1745                        {
1746                                // Add this one to the list
1747                                MaterialEntry* matEntry = new MaterialEntry();
1748                                matEntry->name = name;
1749                                matEntry->xsiShader = XSI::Shader(rtParam.GetSource());
1750                                mXsiMaterialMap[name] = matEntry;
1751                        }
1752                }
1753        }
1754}
Note: See TracBrowser for help on using the repository browser.