mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 12:44:46 +00:00
Added initial behavior for ImageAssets to hold a list of GFX resources of different texture profiles to avoid mem leaks with incorrect-typed usages Added function to ImageAsset to get best-fit asset, allowing for fallbacks if the requested assetID is not found Added function to ShapeAsset to get best-fit asset, allowing for fallbacks if the requested assetID is not found Disabled fields for dynamic and static shadowmap refresh rates Moved noShape model to core/rendering/shapes to place it in a more logical module position Added an include to avoid undefined type compile error and removed unneeded semicolon from zone code Added call to reload probe textures when a reloadTextures call is made Adjusted default directional light shadowmap settings to not be as extreme Added utility function to probe manager to allow any class to request a 'best fit' list of probes that would affect a given location, allowing other classes such as fog or particles to utilize IBL. Also updated probeManager's forward rendering to utilize same function to reduce code duplication. Shifted shape loader code to utilize assimp for loader consistency and testing Changed render bin used for SSAO postfx so it runs at the right time Made Core_Rendering module scan for assets Updated loose file references to a number of assets to follow proper formatting Refactored asset import code to follow a more consistent object heirarchy structure on importing assets, allowing more reliable cross-referencing between inbound items Updated asset import logic for materials/images so that they properly utilize ImageType. Images correctly save out the assigned image type, materials reference the images' type to know what map slot they should be used in. Importer logic also updated to better find-and-add associated images based on type. Cleaned up a bunch of old, outdated code in the asset importer Added initial handling for in-place importing of files without needing to process them through the UI. Added ability to edit module script from RMB context menu if torsion path is set Updated list field code for variable inspector to utilize correct ownerObject field
2464 lines
75 KiB
C++
2464 lines
75 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Copyright (c) 2012 GarageGames, LLC
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "platform/platform.h"
|
|
#include "ts/tsShape.h"
|
|
|
|
#include "ts/tsLastDetail.h"
|
|
#include "ts/tsMaterialList.h"
|
|
#include "core/stringTable.h"
|
|
#include "console/console.h"
|
|
#include "ts/tsShapeInstance.h"
|
|
#include "collision/convex.h"
|
|
#include "materials/matInstance.h"
|
|
#include "materials/materialManager.h"
|
|
#include "math/mathIO.h"
|
|
#include "core/util/endian.h"
|
|
#include "core/stream/fileStream.h"
|
|
#include "console/compiler.h"
|
|
#include "core/fileObject.h"
|
|
|
|
#ifdef TORQUE_COLLADA
|
|
extern TSShape* loadColladaShape(const Torque::Path &path);
|
|
#endif
|
|
|
|
#ifdef TORQUE_ASSIMP
|
|
extern TSShape* assimpLoadShape(const Torque::Path &path);
|
|
#endif
|
|
|
|
/// most recent version -- this is the version we write
|
|
S32 TSShape::smVersion = 28;
|
|
/// the version currently being read...valid only during a read
|
|
S32 TSShape::smReadVersion = -1;
|
|
const U32 TSShape::smMostRecentExporterVersion = DTS_EXPORTER_CURRENT_VERSION;
|
|
|
|
F32 TSShape::smAlphaOutLastDetail = -1.0f;
|
|
F32 TSShape::smAlphaInBillboard = 0.15f;
|
|
F32 TSShape::smAlphaOutBillboard = 0.15f;
|
|
F32 TSShape::smAlphaInDefault = -1.0f;
|
|
F32 TSShape::smAlphaOutDefault = -1.0f;
|
|
|
|
// don't bother even loading this many of the highest detail levels (but
|
|
// always load last renderable detail)
|
|
S32 TSShape::smNumSkipLoadDetails = 0;
|
|
|
|
bool TSShape::smInitOnRead = true;
|
|
bool TSShape::smUseHardwareSkinning = true;
|
|
U32 TSShape::smMaxSkinBones = 70;
|
|
|
|
|
|
TSShape::TSShape()
|
|
{
|
|
materialList = NULL;
|
|
mReadVersion = -1; // -1 means constructed from scratch (e.g., in exporter or no read yet)
|
|
mSequencesConstructed = false;
|
|
mShapeData = NULL;
|
|
mShapeDataSize = 0;
|
|
|
|
mUseDetailFromScreenError = false;
|
|
mNeedReinit = false;
|
|
|
|
mDetailLevelLookup.setSize( 1 );
|
|
mDetailLevelLookup[0].set( -1, 0 );
|
|
|
|
VECTOR_SET_ASSOCIATION(sequences);
|
|
VECTOR_SET_ASSOCIATION(nodeRotations);
|
|
VECTOR_SET_ASSOCIATION(nodeTranslations);
|
|
VECTOR_SET_ASSOCIATION(nodeUniformScales);
|
|
VECTOR_SET_ASSOCIATION(nodeAlignedScales);
|
|
VECTOR_SET_ASSOCIATION(nodeArbitraryScaleRots);
|
|
VECTOR_SET_ASSOCIATION(nodeArbitraryScaleFactors);
|
|
VECTOR_SET_ASSOCIATION(groundRotations);
|
|
VECTOR_SET_ASSOCIATION(groundTranslations);
|
|
VECTOR_SET_ASSOCIATION(triggers);
|
|
VECTOR_SET_ASSOCIATION(billboardDetails);
|
|
VECTOR_SET_ASSOCIATION(detailCollisionAccelerators);
|
|
VECTOR_SET_ASSOCIATION(names);
|
|
|
|
VECTOR_SET_ASSOCIATION( nodes );
|
|
VECTOR_SET_ASSOCIATION( objects );
|
|
VECTOR_SET_ASSOCIATION( objectStates );
|
|
VECTOR_SET_ASSOCIATION( subShapeFirstNode );
|
|
VECTOR_SET_ASSOCIATION( subShapeFirstObject );
|
|
VECTOR_SET_ASSOCIATION( detailFirstSkin );
|
|
VECTOR_SET_ASSOCIATION( subShapeNumNodes );
|
|
VECTOR_SET_ASSOCIATION( subShapeNumObjects );
|
|
VECTOR_SET_ASSOCIATION( details );
|
|
VECTOR_SET_ASSOCIATION( defaultRotations );
|
|
VECTOR_SET_ASSOCIATION( defaultTranslations );
|
|
|
|
VECTOR_SET_ASSOCIATION( subShapeFirstTranslucentObject );
|
|
VECTOR_SET_ASSOCIATION( meshes );
|
|
|
|
VECTOR_SET_ASSOCIATION( alphaIn );
|
|
VECTOR_SET_ASSOCIATION( alphaOut );
|
|
}
|
|
|
|
TSShape::~TSShape()
|
|
{
|
|
delete materialList;
|
|
|
|
S32 i;
|
|
|
|
// everything left over here is a legit mesh
|
|
for (i=0; i<meshes.size(); i++)
|
|
{
|
|
if (!meshes[i])
|
|
continue;
|
|
|
|
// Handle meshes that were either assembled with the shape or added later
|
|
if (((S8*)meshes[i] >= mShapeData) && ((S8*)meshes[i] < (mShapeData + mShapeDataSize)))
|
|
destructInPlace(meshes[i]);
|
|
else
|
|
delete meshes[i];
|
|
}
|
|
|
|
for (i=0; i<billboardDetails.size(); i++)
|
|
{
|
|
delete billboardDetails[i];
|
|
billboardDetails[i] = NULL;
|
|
}
|
|
billboardDetails.clear();
|
|
|
|
// Delete any generated accelerators
|
|
S32 dca;
|
|
for (dca = 0; dca < detailCollisionAccelerators.size(); dca++)
|
|
{
|
|
ConvexHullAccelerator* accel = detailCollisionAccelerators[dca];
|
|
if (accel != NULL) {
|
|
delete [] accel->vertexList;
|
|
delete [] accel->normalList;
|
|
for (S32 j = 0; j < accel->numVerts; j++)
|
|
delete [] accel->emitStrings[j];
|
|
delete [] accel->emitStrings;
|
|
delete accel;
|
|
}
|
|
}
|
|
for (dca = 0; dca < detailCollisionAccelerators.size(); dca++)
|
|
detailCollisionAccelerators[dca] = NULL;
|
|
|
|
if( mShapeData )
|
|
delete[] mShapeData;
|
|
}
|
|
|
|
const String& TSShape::getName( S32 nameIndex ) const
|
|
{
|
|
AssertFatal(nameIndex>=0 && nameIndex<names.size(),"TSShape::getName");
|
|
return names[nameIndex];
|
|
}
|
|
|
|
const String& TSShape::getMeshName( S32 meshIndex ) const
|
|
{
|
|
S32 nameIndex = objects[meshIndex].nameIndex;
|
|
if ( nameIndex < 0 )
|
|
return String::EmptyString;
|
|
|
|
return names[nameIndex];
|
|
}
|
|
|
|
const String& TSShape::getNodeName( S32 nodeIndex ) const
|
|
{
|
|
S32 nameIdx = nodes[nodeIndex].nameIndex;
|
|
if ( nameIdx < 0 )
|
|
return String::EmptyString;
|
|
|
|
return names[nameIdx];
|
|
}
|
|
|
|
const String& TSShape::getSequenceName( S32 seqIndex ) const
|
|
{
|
|
AssertFatal(seqIndex >= 0 && seqIndex<sequences.size(),"TSShape::getSequenceName index beyond range");
|
|
|
|
S32 nameIdx = sequences[seqIndex].nameIndex;
|
|
if ( nameIdx < 0 )
|
|
return String::EmptyString;
|
|
|
|
return names[nameIdx];
|
|
}
|
|
|
|
S32 TSShape::findName(const String &name) const
|
|
{
|
|
for (S32 i=0; i<names.size(); i++)
|
|
{
|
|
if (names[i].equal( name, String::NoCase ))
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
const String& TSShape::getTargetName( S32 mapToNameIndex ) const
|
|
{
|
|
S32 targetCount = materialList->getMaterialNameList().size();
|
|
|
|
if(mapToNameIndex < 0 || mapToNameIndex >= targetCount)
|
|
return String::EmptyString;
|
|
|
|
return materialList->getMaterialNameList()[mapToNameIndex];
|
|
}
|
|
|
|
S32 TSShape::getTargetCount() const
|
|
{
|
|
if(!this)
|
|
return -1;
|
|
|
|
return materialList->getMaterialNameList().size();
|
|
|
|
}
|
|
|
|
S32 TSShape::findNode(S32 nameIndex) const
|
|
{
|
|
for (S32 i=0; i<nodes.size(); i++)
|
|
if (nodes[i].nameIndex==nameIndex)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
S32 TSShape::findObject(S32 nameIndex) const
|
|
{
|
|
for (S32 i=0; i<objects.size(); i++)
|
|
if (objects[i].nameIndex==nameIndex)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
S32 TSShape::findDetail(S32 nameIndex) const
|
|
{
|
|
for (S32 i=0; i<details.size(); i++)
|
|
if (details[i].nameIndex==nameIndex)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
S32 TSShape::findDetailBySize(S32 size) const
|
|
{
|
|
for (S32 i=0; i<details.size(); i++)
|
|
if (details[i].size==size)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
S32 TSShape::findSequence(S32 nameIndex) const
|
|
{
|
|
for (S32 i=0; i<sequences.size(); i++)
|
|
if (sequences[i].nameIndex==nameIndex)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
bool TSShape::findMeshIndex(const String& meshName, S32& objIndex, S32& meshIndex)
|
|
{
|
|
// Determine the object name and detail size from the mesh name
|
|
S32 detailSize = 999;
|
|
objIndex = findObject(String::GetTrailingNumber(meshName, detailSize));
|
|
if (objIndex < 0)
|
|
return false;
|
|
|
|
// Determine the subshape this object belongs to
|
|
S32 subShapeIndex = getSubShapeForObject(objIndex);
|
|
AssertFatal(subShapeIndex < subShapeFirstObject.size(), "Could not find subshape for object!");
|
|
|
|
// Get the detail levels for the subshape
|
|
Vector<S32> validDetails;
|
|
getSubShapeDetails(subShapeIndex, validDetails);
|
|
|
|
// Find the detail with the correct size
|
|
for (meshIndex = 0; meshIndex < validDetails.size(); meshIndex++)
|
|
{
|
|
const TSShape::Detail& det = details[validDetails[meshIndex]];
|
|
if (detailSize == det.size)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TSShape::needsBufferUpdate()
|
|
{
|
|
// No buffer? definitely need an update!
|
|
if (mVertexSize == 0 || mShapeVertexData.size == 0)
|
|
return true;
|
|
|
|
// Check if we have modified vertex data
|
|
for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
|
|
{
|
|
TSMesh *mesh = *iter;
|
|
if (!mesh ||
|
|
(mesh->getMeshType() != TSMesh::StandardMeshType &&
|
|
mesh->getMeshType() != TSMesh::SkinMeshType))
|
|
continue;
|
|
|
|
// NOTE: cant use mVertexData.isReady since that might not be init'd at this stage
|
|
if (mesh->mVertSize == 0)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
TSMesh* TSShape::findMesh(const String& meshName)
|
|
{
|
|
S32 objIndex, meshIndex;
|
|
if (!findMeshIndex(meshName, objIndex, meshIndex))
|
|
return 0;
|
|
return meshes[objects[objIndex].startMeshIndex + meshIndex];
|
|
}
|
|
|
|
S32 TSShape::getSubShapeForNode(S32 nodeIndex)
|
|
{
|
|
for (S32 i = 0; i < subShapeFirstNode.size(); i++)
|
|
{
|
|
S32 start = subShapeFirstNode[i];
|
|
S32 end = start + subShapeNumNodes[i];
|
|
if ((nodeIndex >= start) && (nodeIndex < end))
|
|
return i;;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
S32 TSShape::getSubShapeForObject(S32 objIndex)
|
|
{
|
|
for (S32 i = 0; i < subShapeFirstObject.size(); i++)
|
|
{
|
|
S32 start = subShapeFirstObject[i];
|
|
S32 end = start + subShapeNumObjects[i];
|
|
if ((objIndex >= start) && (objIndex < end))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void TSShape::getSubShapeDetails(S32 subShapeIndex, Vector<S32>& validDetails)
|
|
{
|
|
validDetails.clear();
|
|
for (S32 i = 0; i < details.size(); i++)
|
|
{
|
|
if ((details[i].subShapeNum == subShapeIndex) ||
|
|
(details[i].subShapeNum < 0))
|
|
validDetails.push_back(i);
|
|
}
|
|
}
|
|
|
|
void TSShape::getNodeWorldTransform(S32 nodeIndex, MatrixF* mat) const
|
|
{
|
|
if ( nodeIndex == -1 )
|
|
{
|
|
mat->identity();
|
|
}
|
|
else
|
|
{
|
|
// Calculate the world transform of the given node
|
|
defaultRotations[nodeIndex].getQuatF().setMatrix(mat);
|
|
mat->setPosition(defaultTranslations[nodeIndex]);
|
|
|
|
S32 parentIndex = nodes[nodeIndex].parentIndex;
|
|
while (parentIndex != -1)
|
|
{
|
|
MatrixF mat2(*mat);
|
|
defaultRotations[parentIndex].getQuatF().setMatrix(mat);
|
|
mat->setPosition(defaultTranslations[parentIndex]);
|
|
mat->mul(mat2);
|
|
|
|
parentIndex = nodes[parentIndex].parentIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TSShape::getNodeObjects(S32 nodeIndex, Vector<S32>& nodeObjects)
|
|
{
|
|
for (S32 i = 0; i < objects.size(); i++)
|
|
{
|
|
if ((nodeIndex == -1) || (objects[i].nodeIndex == nodeIndex))
|
|
nodeObjects.push_back(i);
|
|
}
|
|
}
|
|
|
|
void TSShape::getNodeChildren(S32 nodeIndex, Vector<S32>& nodeChildren)
|
|
{
|
|
for (S32 i = 0; i < nodes.size(); i++)
|
|
{
|
|
if (nodes[i].parentIndex == nodeIndex)
|
|
nodeChildren.push_back(i);
|
|
}
|
|
}
|
|
|
|
void TSShape::getObjectDetails(S32 objIndex, Vector<S32>& objDetails)
|
|
{
|
|
// Get the detail levels for this subshape
|
|
Vector<S32> validDetails;
|
|
getSubShapeDetails(getSubShapeForObject(objIndex), validDetails);
|
|
|
|
// Get the non-null details for this object
|
|
const TSShape::Object& obj = objects[objIndex];
|
|
for (S32 i = 0; i < obj.numMeshes; i++)
|
|
{
|
|
if (meshes[obj.startMeshIndex + i])
|
|
objDetails.push_back(validDetails[i]);
|
|
}
|
|
}
|
|
|
|
void TSShape::init()
|
|
{
|
|
initObjects();
|
|
initVertexFeatures();
|
|
initMaterialList();
|
|
mNeedReinit = false;
|
|
}
|
|
|
|
void TSShape::initObjects()
|
|
{
|
|
S32 numSubShapes = subShapeFirstNode.size();
|
|
AssertFatal(numSubShapes == subShapeFirstObject.size(), "TSShape::initObjects");
|
|
|
|
S32 i, j;
|
|
|
|
// set up parent/child relationships on nodes and objects
|
|
for (i = 0; i<nodes.size(); i++)
|
|
nodes[i].firstObject = nodes[i].firstChild = nodes[i].nextSibling = -1;
|
|
for (i = 0; i<nodes.size(); i++)
|
|
{
|
|
S32 parentIndex = nodes[i].parentIndex;
|
|
if (parentIndex >= 0)
|
|
{
|
|
if (nodes[parentIndex].firstChild<0)
|
|
nodes[parentIndex].firstChild = i;
|
|
else
|
|
{
|
|
S32 child = nodes[parentIndex].firstChild;
|
|
while (nodes[child].nextSibling >= 0)
|
|
child = nodes[child].nextSibling;
|
|
nodes[child].nextSibling = i;
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i<objects.size(); i++)
|
|
{
|
|
objects[i].nextSibling = -1;
|
|
|
|
S32 nodeIndex = objects[i].nodeIndex;
|
|
if (nodeIndex >= 0)
|
|
{
|
|
if (nodes[nodeIndex].firstObject<0)
|
|
nodes[nodeIndex].firstObject = i;
|
|
else
|
|
{
|
|
S32 objectIndex = nodes[nodeIndex].firstObject;
|
|
while (objects[objectIndex].nextSibling >= 0)
|
|
objectIndex = objects[objectIndex].nextSibling;
|
|
objects[objectIndex].nextSibling = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
mFlags = 0;
|
|
for (i = 0; i<sequences.size(); i++)
|
|
{
|
|
if (!sequences[i].animatesScale())
|
|
continue;
|
|
|
|
U32 curVal = mFlags & AnyScale;
|
|
U32 newVal = sequences[i].flags & AnyScale;
|
|
mFlags &= ~(AnyScale);
|
|
mFlags |= getMax(curVal, newVal); // take the larger value (can only convert upwards)
|
|
}
|
|
|
|
// set up alphaIn and alphaOut vectors...
|
|
alphaIn.setSize(details.size());
|
|
alphaOut.setSize(details.size());
|
|
|
|
for (i = 0; i<details.size(); i++)
|
|
{
|
|
if (details[i].size<0)
|
|
{
|
|
// we don't care...
|
|
alphaIn[i] = 0.0f;
|
|
alphaOut[i] = 0.0f;
|
|
}
|
|
else if (i + 1 == details.size() || details[i + 1].size<0)
|
|
{
|
|
alphaIn[i] = 0.0f;
|
|
alphaOut[i] = smAlphaOutLastDetail;
|
|
}
|
|
else
|
|
{
|
|
if (details[i + 1].subShapeNum<0)
|
|
{
|
|
// following detail is a billboard detail...treat special...
|
|
alphaIn[i] = smAlphaInBillboard;
|
|
alphaOut[i] = smAlphaOutBillboard;
|
|
}
|
|
else
|
|
{
|
|
// next detail is normal detail
|
|
alphaIn[i] = smAlphaInDefault;
|
|
alphaOut[i] = smAlphaOutDefault;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = mSmallestVisibleDL - 1; i >= 0; i--)
|
|
{
|
|
if (i<smNumSkipLoadDetails)
|
|
{
|
|
// this detail level renders when pixel size
|
|
// is larger than our cap...zap all the meshes and decals
|
|
// associated with it and use the next detail level
|
|
// instead...
|
|
S32 ss = details[i].subShapeNum;
|
|
S32 od = details[i].objectDetailNum;
|
|
|
|
if (ss == details[i + 1].subShapeNum && od == details[i + 1].objectDetailNum)
|
|
// doh! already done this one (init can be called multiple times on same shape due
|
|
// to sequence importing).
|
|
continue;
|
|
details[i].subShapeNum = details[i + 1].subShapeNum;
|
|
details[i].objectDetailNum = details[i + 1].objectDetailNum;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i<details.size(); i++)
|
|
{
|
|
S32 count = 0;
|
|
S32 ss = details[i].subShapeNum;
|
|
S32 od = details[i].objectDetailNum;
|
|
if (ss<0)
|
|
{
|
|
// billboard detail...
|
|
details[i].polyCount = 2;
|
|
continue;
|
|
}
|
|
S32 start = subShapeFirstObject[ss];
|
|
S32 end = start + subShapeNumObjects[ss];
|
|
for (j = start; j<end; j++)
|
|
{
|
|
Object & obj = objects[j];
|
|
if (od<obj.numMeshes)
|
|
{
|
|
TSMesh * mesh = meshes[obj.startMeshIndex + od];
|
|
count += mesh ? mesh->getNumPolys() : 0;
|
|
}
|
|
}
|
|
details[i].polyCount = count;
|
|
}
|
|
|
|
// Init the collision accelerator array. Note that we don't compute the
|
|
// accelerators until the app requests them
|
|
{
|
|
S32 dca;
|
|
for (dca = 0; dca < detailCollisionAccelerators.size(); dca++)
|
|
{
|
|
ConvexHullAccelerator* accel = detailCollisionAccelerators[dca];
|
|
if (accel != NULL) {
|
|
delete[] accel->vertexList;
|
|
delete[] accel->normalList;
|
|
for (S32 vertID = 0; vertID < accel->numVerts; vertID++)
|
|
delete[] accel->emitStrings[vertID];
|
|
delete[] accel->emitStrings;
|
|
delete accel;
|
|
}
|
|
}
|
|
|
|
detailCollisionAccelerators.setSize(details.size());
|
|
for (dca = 0; dca < detailCollisionAccelerators.size(); dca++)
|
|
detailCollisionAccelerators[dca] = NULL;
|
|
}
|
|
|
|
// Assign mesh parents & format
|
|
for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
|
|
{
|
|
TSMesh *mesh = *iter;
|
|
if (!mesh)
|
|
continue;
|
|
|
|
if (mesh->mParentMesh >= meshes.size())
|
|
{
|
|
Con::warnf("Mesh %i has a bad parentMeshObject (%i)", iter - meshes.begin(), mesh->mParentMesh);
|
|
}
|
|
|
|
if (mesh->mParentMesh >= 0 && mesh->mParentMesh < meshes.size())
|
|
{
|
|
mesh->mParentMeshObject = meshes[mesh->mParentMesh];
|
|
}
|
|
else
|
|
{
|
|
mesh->mParentMeshObject = NULL;
|
|
}
|
|
|
|
mesh->mVertexFormat = &mVertexFormat;
|
|
}
|
|
}
|
|
|
|
void TSShape::initVertexBuffers()
|
|
{
|
|
// Assumes mVertexData is valid
|
|
if (!mShapeVertexData.vertexDataReady)
|
|
{
|
|
AssertFatal(false, "WTF");
|
|
}
|
|
|
|
U32 destIndices = 0;
|
|
U32 destPrims = 0;
|
|
|
|
for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
|
|
{
|
|
TSMesh *mesh = *iter;
|
|
if (!mesh ||
|
|
(mesh->getMeshType() != TSMesh::StandardMeshType &&
|
|
mesh->getMeshType() != TSMesh::SkinMeshType))
|
|
continue;
|
|
|
|
destIndices += mesh->mIndices.size();
|
|
destPrims += mesh->mPrimitives.size();
|
|
}
|
|
|
|
// For HW skinning we can just use the static buffer
|
|
if (TSShape::smUseHardwareSkinning)
|
|
{
|
|
getVertexBuffer(mShapeVertexBuffer, GFXBufferTypeStatic);
|
|
}
|
|
|
|
// Also the IBO
|
|
mShapeVertexIndices.set(GFX, destIndices, destPrims, GFXBufferTypeStatic);
|
|
U16 *indicesStart = NULL;
|
|
mShapeVertexIndices.lock(&indicesStart, NULL);
|
|
U16 *ibIndices = indicesStart;
|
|
GFXPrimitive *piInput = mShapeVertexIndices->mPrimitiveArray;
|
|
U32 vertStart = 0;
|
|
U32 primStart = 0;
|
|
U32 indStart = 0;
|
|
|
|
// Create VBO
|
|
for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
|
|
{
|
|
TSMesh *mesh = *iter;
|
|
if (!mesh ||
|
|
(mesh->getMeshType() != TSMesh::StandardMeshType &&
|
|
mesh->getMeshType() != TSMesh::SkinMeshType))
|
|
continue;
|
|
|
|
// Make the offset vbo
|
|
mesh->mPrimBufferOffset = primStart;
|
|
|
|
// Dump primitives to locked buffer
|
|
mesh->dumpPrimitives(vertStart, indStart, piInput, ibIndices);
|
|
|
|
AssertFatal(mesh->mVertOffset / mVertexSize == vertStart, "offset mismatch");
|
|
|
|
vertStart += mesh->mNumVerts;
|
|
primStart += mesh->mPrimitives.size();
|
|
indStart += mesh->mIndices.size();
|
|
|
|
mesh->mVB = mShapeVertexBuffer;
|
|
mesh->mPB = mShapeVertexIndices;
|
|
|
|
// Advance
|
|
piInput += mesh->mPrimitives.size();
|
|
ibIndices += mesh->mIndices.size();
|
|
|
|
if (TSSkinMesh::smDebugSkinVerts && mesh->getMeshType() == TSMesh::SkinMeshType)
|
|
{
|
|
static_cast<TSSkinMesh*>(mesh)->printVerts();
|
|
}
|
|
}
|
|
|
|
#ifdef TORQUE_DEBUG
|
|
// Verify prims
|
|
if (TSSkinMesh::smDebugSkinVerts)
|
|
{
|
|
U32 vertsInBuffer = mShapeVertexData.size / mVertexSize;
|
|
U32 indsInBuffer = ibIndices - indicesStart;
|
|
|
|
for (U32 primID = 0; primID < primStart; primID++)
|
|
{
|
|
GFXPrimitive &prim = mShapeVertexIndices->mPrimitiveArray[primID];
|
|
|
|
if (prim.type != GFXTriangleList && prim.type != GFXTriangleStrip)
|
|
{
|
|
AssertFatal(false, "Unexpected triangle list");
|
|
}
|
|
|
|
if (prim.type == GFXTriangleStrip)
|
|
continue;
|
|
|
|
AssertFatal(prim.startVertex < vertsInBuffer, "wrong start vertex");
|
|
AssertFatal((prim.startVertex + prim.numVertices) <= vertsInBuffer, "too many verts");
|
|
AssertFatal(prim.startIndex + (prim.numPrimitives * 3) <= indsInBuffer, "too many inds");
|
|
|
|
for (U32 i = prim.startIndex; i < prim.startIndex + (prim.numPrimitives * 3); i++)
|
|
{
|
|
if (indicesStart[i] >= vertsInBuffer)
|
|
{
|
|
AssertFatal(false, "vert not in buffer");
|
|
}
|
|
U16 idx = indicesStart[i];
|
|
if (idx < prim.minIndex)
|
|
{
|
|
AssertFatal(false, "index out of minIndex range");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
mShapeVertexIndices.unlock();
|
|
}
|
|
|
|
void TSShape::getVertexBuffer(TSVertexBufferHandle &vb, GFXBufferType bufferType)
|
|
{
|
|
vb.set(GFX, mVertexSize, &mVertexFormat, mShapeVertexData.size / mVertexSize, bufferType);
|
|
|
|
U8 *vertPtr = vb.lock();
|
|
dMemcpy(vertPtr, mShapeVertexData.base, mShapeVertexData.size);
|
|
vb.unlock();
|
|
}
|
|
|
|
void TSShape::initVertexBufferPointers()
|
|
{
|
|
if (mBasicVertexFormat.vertexSize == -1)
|
|
return;
|
|
AssertFatal(mVertexSize == mBasicVertexFormat.vertexSize, "vertex size mismatch");
|
|
|
|
for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
|
|
{
|
|
TSMesh *mesh = *iter;
|
|
if (mesh &&
|
|
(mesh->getMeshType() == TSMesh::StandardMeshType ||
|
|
mesh->getMeshType() == TSMesh::SkinMeshType))
|
|
{
|
|
// Set buffer
|
|
AssertFatal(mesh->mNumVerts == 0 || mesh->mNumVerts >= mesh->vertsPerFrame, "invalid verts per frame");
|
|
if (mesh->mVertSize > 0 && !mesh->mVertexData.isReady())
|
|
{
|
|
U32 boneOffset = 0;
|
|
U32 texCoordOffset = 0;
|
|
AssertFatal(mesh->mVertSize == mVertexFormat.getSizeInBytes(), "mismatch in format size");
|
|
|
|
if (mBasicVertexFormat.boneOffset >= 0)
|
|
{
|
|
boneOffset = mBasicVertexFormat.boneOffset;
|
|
}
|
|
|
|
if (mBasicVertexFormat.texCoordOffset >= 0)
|
|
{
|
|
texCoordOffset = mBasicVertexFormat.texCoordOffset;
|
|
}
|
|
|
|
// Initialize the vertex data
|
|
mesh->mVertexData.set(mShapeVertexData.base + mesh->mVertOffset, mesh->mVertSize, mesh->mNumVerts, texCoordOffset, boneOffset, false);
|
|
mesh->mVertexData.setReady(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TSShape::initVertexFeatures()
|
|
{
|
|
|
|
if (!needsBufferUpdate())
|
|
{
|
|
// Init format from basic format
|
|
mVertexFormat.clear();
|
|
mBasicVertexFormat.getFormat(mVertexFormat);
|
|
mVertexSize = mVertexFormat.getSizeInBytes();
|
|
|
|
initVertexBufferPointers();
|
|
|
|
for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
|
|
{
|
|
TSMesh *mesh = *iter;
|
|
if (mesh &&
|
|
(mesh->getMeshType() == TSMesh::SkinMeshType))
|
|
{
|
|
static_cast<TSSkinMesh*>(mesh)->createSkinBatchData();
|
|
}
|
|
}
|
|
|
|
// Make sure VBO is init'd
|
|
initVertexBuffers();
|
|
return;
|
|
}
|
|
|
|
// Cleanout VBO
|
|
mShapeVertexBuffer = NULL;
|
|
|
|
// Make sure mesh has verts stored in mesh data, we're recreating the buffer
|
|
TSBasicVertexFormat basicFormat;
|
|
|
|
initVertexBufferPointers();
|
|
|
|
for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
|
|
{
|
|
TSMesh *mesh = *iter;
|
|
if (mesh &&
|
|
(mesh->getMeshType() == TSMesh::StandardMeshType ||
|
|
mesh->getMeshType() == TSMesh::SkinMeshType))
|
|
{
|
|
// Make sure we have everything in the vert lists
|
|
mesh->makeEditable();
|
|
|
|
// We need the skin batching data here to determine bone counts
|
|
if (mesh->getMeshType() == TSMesh::SkinMeshType)
|
|
{
|
|
static_cast<TSSkinMesh*>(mesh)->createSkinBatchData();
|
|
}
|
|
|
|
basicFormat.addMeshRequirements(mesh);
|
|
}
|
|
}
|
|
|
|
mVertexFormat.clear();
|
|
mBasicVertexFormat = basicFormat;
|
|
mBasicVertexFormat.getFormat(mVertexFormat);
|
|
mBasicVertexFormat.vertexSize = mVertexFormat.getSizeInBytes();
|
|
mVertexSize = mBasicVertexFormat.vertexSize;
|
|
|
|
U32 destVertex = 0;
|
|
U32 destIndices = 0;
|
|
|
|
// Go fix up meshes to include defaults for optional features
|
|
// and initialize them if they're not a skin mesh.
|
|
U32 count = 0;
|
|
for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
|
|
{
|
|
TSMesh *mesh = *iter;
|
|
if (!mesh ||
|
|
(mesh->getMeshType() != TSMesh::StandardMeshType &&
|
|
mesh->getMeshType() != TSMesh::SkinMeshType))
|
|
continue;
|
|
|
|
mesh->mVertSize = mVertexSize;
|
|
mesh->mVertOffset = destVertex;
|
|
|
|
destVertex += mesh->mVertSize * mesh->getNumVerts();
|
|
destIndices += mesh->mIndices.size();
|
|
|
|
count += 1;
|
|
}
|
|
|
|
// Don't set up if we have no meshes
|
|
if (count == 0)
|
|
{
|
|
mShapeVertexData.set(NULL, 0);
|
|
mShapeVertexData.vertexDataReady = false;
|
|
return;
|
|
}
|
|
|
|
// Now we can create the VBO
|
|
mShapeVertexData.set(NULL, 0);
|
|
U8 *vertexData = (U8*)dMalloc_aligned(destVertex, 16);
|
|
U8 *vertexDataPtr = vertexData;
|
|
mShapeVertexData.set(vertexData, destVertex);
|
|
|
|
// Create VBO
|
|
for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
|
|
{
|
|
TSMesh *mesh = *iter;
|
|
|
|
if (!mesh ||
|
|
(mesh->getMeshType() != TSMesh::StandardMeshType &&
|
|
mesh->getMeshType() != TSMesh::SkinMeshType))
|
|
continue;
|
|
|
|
U32 boneOffset = 0;
|
|
U32 texCoordOffset = 0;
|
|
AssertFatal(mesh->mVertSize == mVertexFormat.getSizeInBytes(), "mismatch in format size");
|
|
|
|
if (mBasicVertexFormat.boneOffset >= 0)
|
|
{
|
|
boneOffset = mBasicVertexFormat.boneOffset;
|
|
}
|
|
|
|
if (mBasicVertexFormat.texCoordOffset >= 0)
|
|
{
|
|
texCoordOffset = mBasicVertexFormat.texCoordOffset;
|
|
}
|
|
|
|
// Dump everything
|
|
mesh->mVertexData.setReady(false);
|
|
mesh->mVertSize = mVertexSize;
|
|
AssertFatal(mesh->mVertOffset == vertexDataPtr - vertexData, "vertex offset mismatch");
|
|
mesh->mNumVerts = mesh->getNumVerts();
|
|
|
|
// Correct bad meshes
|
|
if (mesh->mNumVerts != 0 && mesh->vertsPerFrame > mesh->mNumVerts)
|
|
{
|
|
Con::warnf("Shape mesh has bad vertsPerFrame (%i, should be <= %i)", mesh->vertsPerFrame, mesh->mNumVerts);
|
|
mesh->vertsPerFrame = mesh->mNumVerts;
|
|
}
|
|
|
|
mesh->mVertexData.set(mShapeVertexData.base + mesh->mVertOffset, mesh->mVertSize, mesh->mNumVerts, texCoordOffset, boneOffset, false);
|
|
mesh->convertToVertexData();
|
|
mesh->mVertexData.setReady(true);
|
|
|
|
#ifdef TORQUE_DEBUG
|
|
AssertFatal(mesh->mNumVerts == mesh->mVerts.size(), "vert mismatch");
|
|
for (U32 i = 0; i < mesh->mNumVerts; i++)
|
|
{
|
|
Point3F v1 = mesh->mVerts[i];
|
|
Point3F v2 = mesh->mVertexData.getBase(i).vert();
|
|
AssertFatal(mesh->mVerts[i] == mesh->mVertexData.getBase(i).vert(), "vert data mismatch");
|
|
}
|
|
|
|
if (mesh->getMeshType() == TSMesh::SkinMeshType)
|
|
{
|
|
AssertFatal(mesh->getMaxBonesPerVert() != 0, "Skin mesh has no bones used, very strange!");
|
|
}
|
|
#endif
|
|
|
|
// Advance
|
|
vertexDataPtr += mesh->mVertSize * mesh->mNumVerts;
|
|
|
|
AssertFatal(vertexDataPtr - vertexData <= destVertex, "Vertex data overflow");
|
|
}
|
|
|
|
mShapeVertexData.vertexDataReady = true;
|
|
|
|
initVertexBuffers();
|
|
}
|
|
|
|
void TSShape::setupBillboardDetails( const String &cachePath )
|
|
{
|
|
// set up billboard details -- only do this once, meaning that
|
|
// if we add a sequence to the shape we don't redo the billboard
|
|
// details...
|
|
if ( !billboardDetails.empty() )
|
|
return;
|
|
|
|
for ( U32 i=0; i < details.size(); i++ )
|
|
{
|
|
const Detail &det = details[i];
|
|
|
|
if ( det.subShapeNum >= 0 )
|
|
continue; // not a billboard detail
|
|
|
|
while (billboardDetails.size() <= i )
|
|
billboardDetails.push_back(NULL);
|
|
|
|
billboardDetails[i] = new TSLastDetail( this,
|
|
cachePath,
|
|
det.bbEquatorSteps,
|
|
det.bbPolarSteps,
|
|
det.bbPolarAngle,
|
|
det.bbIncludePoles,
|
|
det.bbDetailLevel,
|
|
det.bbDimension );
|
|
|
|
billboardDetails[i]->update();
|
|
}
|
|
}
|
|
|
|
void TSShape::initMaterialList()
|
|
{
|
|
S32 numSubShapes = subShapeFirstObject.size();
|
|
#if defined(TORQUE_MAX_LIB)
|
|
subShapeFirstTranslucentObject.setSize(numSubShapes);
|
|
#endif
|
|
|
|
S32 i,j,k;
|
|
// for each subshape, find the first translucent object
|
|
// also, while we're at it, set mHasTranslucency
|
|
for (S32 ss = 0; ss<numSubShapes; ss++)
|
|
{
|
|
S32 start = subShapeFirstObject[ss];
|
|
S32 end = subShapeNumObjects[ss];
|
|
subShapeFirstTranslucentObject[ss] = end;
|
|
for (i=start; i<end; i++)
|
|
{
|
|
// check to see if this object has translucency
|
|
Object & obj = objects[i];
|
|
for (j=0; j<obj.numMeshes; j++)
|
|
{
|
|
TSMesh * mesh = meshes[obj.startMeshIndex+j];
|
|
if (!mesh)
|
|
continue;
|
|
|
|
for (k=0; k<mesh->mPrimitives.size(); k++)
|
|
{
|
|
if (mesh->mPrimitives[k].matIndex & TSDrawPrimitive::NoMaterial)
|
|
continue;
|
|
S32 flags = materialList->getFlags(mesh->mPrimitives[k].matIndex & TSDrawPrimitive::MaterialMask);
|
|
if (flags & TSMaterialList::AuxiliaryMap)
|
|
continue;
|
|
if (flags & TSMaterialList::Translucent)
|
|
{
|
|
mFlags |= HasTranslucency;
|
|
subShapeFirstTranslucentObject[ss] = i;
|
|
break;
|
|
}
|
|
}
|
|
if (k!=mesh->mPrimitives.size())
|
|
break;
|
|
}
|
|
if (j!=obj.numMeshes)
|
|
break;
|
|
}
|
|
if (i!=end)
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
bool TSShape::preloadMaterialList(const Torque::Path &path)
|
|
{
|
|
if (materialList)
|
|
materialList->setTextureLookupPath(path.getPath());
|
|
return true;
|
|
}
|
|
|
|
bool TSShape::buildConvexHull(S32 dl) const
|
|
{
|
|
AssertFatal(dl>=0 && dl<details.size(),"TSShape::buildConvexHull: detail out of range");
|
|
|
|
bool ok = true;
|
|
|
|
const Detail & detail = details[dl];
|
|
S32 ss = detail.subShapeNum;
|
|
S32 od = detail.objectDetailNum;
|
|
|
|
S32 start = subShapeFirstObject[ss];
|
|
S32 end = subShapeNumObjects[ss];
|
|
for (S32 i=start; i<end; i++)
|
|
{
|
|
TSMesh * mesh = meshes[objects[i].startMeshIndex+od];
|
|
if (!mesh)
|
|
continue;
|
|
ok &= mesh->buildConvexHull();
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
Vector<MatrixF> gTempNodeTransforms(__FILE__, __LINE__);
|
|
|
|
void TSShape::computeBounds(S32 dl, Box3F & bounds) const
|
|
{
|
|
// if dl==-1, nothing to do
|
|
if (dl==-1)
|
|
return;
|
|
|
|
AssertFatal(dl>=0 && dl<details.size(),"TSShapeInstance::computeBounds");
|
|
|
|
// get subshape and object detail
|
|
const TSDetail * detail = &details[dl];
|
|
S32 ss = detail->subShapeNum;
|
|
S32 od = detail->objectDetailNum;
|
|
|
|
// If we have no subshapes then there is
|
|
// no valid bounds for this detail level.
|
|
if ( ss < 0 )
|
|
return;
|
|
|
|
// set up temporary storage for non-local transforms...
|
|
S32 i;
|
|
S32 start = subShapeFirstNode[ss];
|
|
S32 end = subShapeNumNodes[ss] + start;
|
|
gTempNodeTransforms.setSize(end-start);
|
|
for (i=start; i<end; i++)
|
|
{
|
|
MatrixF mat;
|
|
QuatF q;
|
|
TSTransform::setMatrix(defaultRotations[i].getQuatF(&q),defaultTranslations[i],&mat);
|
|
if (nodes[i].parentIndex>=0)
|
|
gTempNodeTransforms[i-start].mul(gTempNodeTransforms[nodes[i].parentIndex-start],mat);
|
|
else
|
|
gTempNodeTransforms[i-start] = mat;
|
|
}
|
|
|
|
// run through objects and updating bounds as we go
|
|
bounds.minExtents.set( 10E30f, 10E30f, 10E30f);
|
|
bounds.maxExtents.set(-10E30f,-10E30f,-10E30f);
|
|
Box3F box;
|
|
start = subShapeFirstObject[ss];
|
|
end = subShapeNumObjects[ss] + start;
|
|
for (i=start; i<end; i++)
|
|
{
|
|
const Object * object = &objects[i];
|
|
TSMesh * mesh = od<object->numMeshes ? meshes[object->startMeshIndex+od] : NULL;
|
|
if (mesh)
|
|
{
|
|
static MatrixF idMat(true);
|
|
if (object->nodeIndex<0)
|
|
mesh->computeBounds(idMat,box);
|
|
else
|
|
mesh->computeBounds(gTempNodeTransforms[object->nodeIndex-start],box);
|
|
bounds.minExtents.setMin(box.minExtents);
|
|
bounds.maxExtents.setMax(box.maxExtents);
|
|
}
|
|
}
|
|
}
|
|
|
|
TSShapeAlloc TSShape::smTSAlloc;
|
|
|
|
#define tsalloc TSShape::smTSAlloc
|
|
|
|
|
|
// messy stuff: check to see if we should "skip" meshNum
|
|
// this assumes that meshes for a given object are in a row
|
|
// skipDL is the lowest detail number we keep (i.e., the # of details we skip)
|
|
bool TSShape::checkSkip(S32 meshNum, S32 & curObject, S32 skipDL)
|
|
{
|
|
if (skipDL==0)
|
|
// easy out...
|
|
return false;
|
|
|
|
// skip detail level exists on this subShape
|
|
S32 skipSS = details[skipDL].subShapeNum;
|
|
|
|
if (curObject<objects.size())
|
|
{
|
|
S32 start = objects[curObject].startMeshIndex;
|
|
if (meshNum>=start)
|
|
{
|
|
// we are either from this object, the next object, or a decal
|
|
if (meshNum < start + objects[curObject].numMeshes)
|
|
{
|
|
// this object...
|
|
if (subShapeFirstObject[skipSS]>curObject)
|
|
// haven't reached this subshape yet
|
|
return true;
|
|
if (skipSS+1==subShapeFirstObject.size() || curObject<subShapeFirstObject[skipSS+1])
|
|
// curObject is on subshape of skip detail...make sure it's after skipDL
|
|
return (meshNum-start<details[skipDL].objectDetailNum);
|
|
// if we get here, then curObject occurs on subShape after skip detail (so keep it)
|
|
return false;
|
|
}
|
|
else
|
|
// advance object, try again
|
|
return checkSkip(meshNum,++curObject,skipDL);
|
|
}
|
|
}
|
|
|
|
AssertFatal(0,"TSShape::checkSkip: assertion failed");
|
|
return false;
|
|
}
|
|
|
|
void TSShape::assembleShape()
|
|
{
|
|
S32 i,j;
|
|
|
|
// get counts...
|
|
S32 numNodes = tsalloc.get32();
|
|
S32 numObjects = tsalloc.get32();
|
|
S32 numDecals = tsalloc.get32();
|
|
S32 numSubShapes = tsalloc.get32();
|
|
S32 numIflMaterials = tsalloc.get32();
|
|
S32 numNodeRots;
|
|
S32 numNodeTrans;
|
|
S32 numNodeUniformScales;
|
|
S32 numNodeAlignedScales;
|
|
S32 numNodeArbitraryScales;
|
|
if (smReadVersion<22)
|
|
{
|
|
numNodeRots = numNodeTrans = tsalloc.get32() - numNodes;
|
|
numNodeUniformScales = numNodeAlignedScales = numNodeArbitraryScales = 0;
|
|
}
|
|
else
|
|
{
|
|
numNodeRots = tsalloc.get32();
|
|
numNodeTrans = tsalloc.get32();
|
|
numNodeUniformScales = tsalloc.get32();
|
|
numNodeAlignedScales = tsalloc.get32();
|
|
numNodeArbitraryScales = tsalloc.get32();
|
|
}
|
|
S32 numGroundFrames = 0;
|
|
if (smReadVersion>23)
|
|
numGroundFrames = tsalloc.get32();
|
|
S32 numObjectStates = tsalloc.get32();
|
|
S32 numDecalStates = tsalloc.get32();
|
|
S32 numTriggers = tsalloc.get32();
|
|
S32 numDetails = tsalloc.get32();
|
|
S32 numMeshes = tsalloc.get32();
|
|
S32 numSkins = 0;
|
|
if (smReadVersion<23)
|
|
// in later versions, skins are kept with other meshes
|
|
numSkins = tsalloc.get32();
|
|
S32 numNames = tsalloc.get32();
|
|
|
|
// Note that we are recalculating these values later on for safety.
|
|
mSmallestVisibleSize = (F32)tsalloc.get32();
|
|
mSmallestVisibleDL = tsalloc.get32();
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
// get bounds...
|
|
tsalloc.get32((S32*)&mRadius,1);
|
|
tsalloc.get32((S32*)&tubeRadius,1);
|
|
tsalloc.get32((S32*)¢er,3);
|
|
tsalloc.get32((S32*)&mBounds,6);
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
// copy various vectors...
|
|
S32 * ptr32 = tsalloc.copyToShape32(numNodes*5);
|
|
nodes.set(ptr32,numNodes);
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
ptr32 = tsalloc.copyToShape32(numObjects*6,true);
|
|
if (!ptr32)
|
|
ptr32 = tsalloc.allocShape32(numSkins*6); // pre v23 shapes store skins and meshes separately...no longer
|
|
else
|
|
tsalloc.allocShape32(numSkins*6);
|
|
objects.set(ptr32,numObjects);
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
// DEPRECATED decals
|
|
ptr32 = tsalloc.getPointer32(numDecals*5);
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
// DEPRECATED ifl materials
|
|
ptr32 = tsalloc.copyToShape32(numIflMaterials*5);
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
ptr32 = tsalloc.copyToShape32(numSubShapes,true);
|
|
subShapeFirstNode.set(ptr32,numSubShapes);
|
|
ptr32 = tsalloc.copyToShape32(numSubShapes,true);
|
|
subShapeFirstObject.set(ptr32,numSubShapes);
|
|
// DEPRECATED subShapeFirstDecal
|
|
ptr32 = tsalloc.getPointer32(numSubShapes);
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
ptr32 = tsalloc.copyToShape32(numSubShapes);
|
|
subShapeNumNodes.set(ptr32,numSubShapes);
|
|
ptr32 = tsalloc.copyToShape32(numSubShapes);
|
|
subShapeNumObjects.set(ptr32,numSubShapes);
|
|
// DEPRECATED subShapeNumDecals
|
|
ptr32 = tsalloc.getPointer32(numSubShapes);
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
ptr32 = tsalloc.allocShape32(numSubShapes);
|
|
subShapeFirstTranslucentObject.set(ptr32,numSubShapes);
|
|
|
|
// get default translation and rotation
|
|
S16 * ptr16 = tsalloc.allocShape16(0);
|
|
for (i=0;i<numNodes;i++)
|
|
tsalloc.copyToShape16(4);
|
|
defaultRotations.set(ptr16,numNodes);
|
|
tsalloc.align32();
|
|
ptr32 = tsalloc.allocShape32(0);
|
|
for (i=0;i<numNodes;i++)
|
|
{
|
|
tsalloc.copyToShape32(3);
|
|
tsalloc.copyToShape32(sizeof(Point3F)-12); // handle alignment issues w/ point3f
|
|
}
|
|
defaultTranslations.set(ptr32,numNodes);
|
|
|
|
// get any node sequence data stored in shape
|
|
nodeTranslations.setSize(numNodeTrans);
|
|
for (i=0;i<numNodeTrans;i++)
|
|
tsalloc.get32((S32*)&nodeTranslations[i],3);
|
|
nodeRotations.setSize(numNodeRots);
|
|
for (i=0;i<numNodeRots;i++)
|
|
tsalloc.get16((S16*)&nodeRotations[i],4);
|
|
tsalloc.align32();
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
if (smReadVersion>21)
|
|
{
|
|
// more node sequence data...scale
|
|
nodeUniformScales.setSize(numNodeUniformScales);
|
|
for (i=0;i<numNodeUniformScales;i++)
|
|
tsalloc.get32((S32*)&nodeUniformScales[i],1);
|
|
nodeAlignedScales.setSize(numNodeAlignedScales);
|
|
for (i=0;i<numNodeAlignedScales;i++)
|
|
tsalloc.get32((S32*)&nodeAlignedScales[i],3);
|
|
nodeArbitraryScaleFactors.setSize(numNodeArbitraryScales);
|
|
for (i=0;i<numNodeArbitraryScales;i++)
|
|
tsalloc.get32((S32*)&nodeArbitraryScaleFactors[i],3);
|
|
nodeArbitraryScaleRots.setSize(numNodeArbitraryScales);
|
|
for (i=0;i<numNodeArbitraryScales;i++)
|
|
tsalloc.get16((S16*)&nodeArbitraryScaleRots[i],4);
|
|
tsalloc.align32();
|
|
|
|
tsalloc.checkGuard();
|
|
}
|
|
|
|
// old shapes need ground transforms moved to ground arrays...but only do it once
|
|
if (smReadVersion<22 && tsalloc.allocShape32(0))
|
|
{
|
|
for (i=0; i<sequences.size(); i++)
|
|
{
|
|
// move ground transform data to ground vectors
|
|
Sequence & seq = sequences[i];
|
|
S32 oldSz = groundTranslations.size();
|
|
groundTranslations.setSize(oldSz+seq.numGroundFrames);
|
|
groundRotations.setSize(oldSz+seq.numGroundFrames);
|
|
for (S32 groundFrm =0; groundFrm<seq.numGroundFrames; groundFrm++)
|
|
{
|
|
groundTranslations[groundFrm +oldSz] = nodeTranslations[seq.firstGroundFrame+ groundFrm -numNodes];
|
|
groundRotations[groundFrm +oldSz] = nodeRotations[seq.firstGroundFrame+ groundFrm -numNodes];
|
|
}
|
|
seq.firstGroundFrame = oldSz;
|
|
seq.baseTranslation -= numNodes;
|
|
seq.baseRotation -= numNodes;
|
|
seq.baseScale = 0; // not used on older shapes...but keep it clean
|
|
}
|
|
}
|
|
|
|
// version 22 & 23 shapes accidentally had no ground transforms, and ground for
|
|
// earlier shapes is handled just above, so...
|
|
if (smReadVersion>23)
|
|
{
|
|
groundTranslations.setSize(numGroundFrames);
|
|
for (i=0;i<numGroundFrames;i++)
|
|
tsalloc.get32((S32*)&groundTranslations[i],3);
|
|
groundRotations.setSize(numGroundFrames);
|
|
for (i=0;i<numGroundFrames;i++)
|
|
tsalloc.get16((S16*)&groundRotations[i],4);
|
|
tsalloc.align32();
|
|
|
|
tsalloc.checkGuard();
|
|
}
|
|
|
|
// object states
|
|
ptr32 = tsalloc.copyToShape32(numObjectStates*3);
|
|
objectStates.set(ptr32,numObjectStates);
|
|
tsalloc.allocShape32(numSkins*3); // provide buffer after objectStates for older shapes
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
// DEPRECATED decal states
|
|
ptr32 = tsalloc.getPointer32(numDecalStates);
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
// frame triggers
|
|
ptr32 = tsalloc.getPointer32(numTriggers*2);
|
|
triggers.setSize(numTriggers);
|
|
dMemcpy(triggers.address(),ptr32,sizeof(S32)*numTriggers*2);
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
// details
|
|
if ( smReadVersion >= 26 )
|
|
{
|
|
U32 alignedSize32 = sizeof( Detail ) / 4;
|
|
ptr32 = tsalloc.copyToShape32( numDetails * alignedSize32, true );
|
|
details.set( ptr32, numDetails );
|
|
}
|
|
else
|
|
{
|
|
// Previous to version 26 the Detail structure
|
|
// only contained the first 7 values...
|
|
//
|
|
// struct Detail
|
|
// {
|
|
// S32 nameIndex;
|
|
// S32 subShapeNum;
|
|
// S32 objectDetailNum;
|
|
// F32 size;
|
|
// F32 averageError;
|
|
// F32 maxError;
|
|
// S32 polyCount;
|
|
// };
|
|
//
|
|
// In the code below we're reading just these 7 values and
|
|
// copying them to the new larger structure.
|
|
|
|
ptr32 = tsalloc.copyToShape32( numDetails * 7, true );
|
|
|
|
details.setSize( numDetails );
|
|
for ( U32 detID = 0; detID < details.size(); detID++, ptr32 += 7 )
|
|
{
|
|
Detail *det = &(details[detID]);
|
|
|
|
// Clear the struct... we don't want to leave
|
|
// garbage in the parts that are unfilled.
|
|
U32 alignedSize32 = sizeof( Detail );
|
|
dMemset( det, 0, alignedSize32 );
|
|
|
|
// Copy the old struct values over.
|
|
dMemcpy( det, ptr32, 7 * 4 );
|
|
|
|
// If this is an autobillboard then we need to
|
|
// fill in the new part of the struct.
|
|
if ( det->subShapeNum >= 0 )
|
|
continue;
|
|
|
|
S32 lastDetailOpts = det->objectDetailNum;
|
|
det->bbEquatorSteps = lastDetailOpts & 0x7F; // bits 0..6
|
|
det->bbPolarSteps = (lastDetailOpts >> 7) & 0x3F; // bits 7..12
|
|
det->bbPolarAngle = 0.5f * M_PI_F * (1.0f/64.0f) * (F32) (( lastDetailOpts >>13 ) & 0x3F); // bits 13..18
|
|
det->bbDetailLevel = (lastDetailOpts >> 19) & 0x0F; // 19..22
|
|
det->bbDimension = (lastDetailOpts >> 23) & 0xFF; // 23..30
|
|
det->bbIncludePoles = (lastDetailOpts & 0x80000000)!=0; // bit 31
|
|
}
|
|
}
|
|
|
|
// Some DTS exporters (MAX - I'm looking at you!) write garbage into the
|
|
// averageError and maxError values which stops LOD from working correctly.
|
|
// Try to detect and fix it
|
|
for ( U32 erID = 0; erID < details.size(); erID++ )
|
|
{
|
|
if ( ( details[erID].averageError == 0 ) || ( details[erID].averageError > 10000 ) ||
|
|
( details[erID].maxError == 0 ) || ( details[erID].maxError > 10000 ) )
|
|
{
|
|
details[erID].averageError = details[erID].maxError = -1.0f;
|
|
}
|
|
}
|
|
|
|
// We don't trust the value of mSmallestVisibleDL loaded from the dts
|
|
// since some legacy meshes seem to have the wrong value. Recalculate it
|
|
// now that we have the details loaded.
|
|
updateSmallestVisibleDL();
|
|
|
|
S32 skipDL = getMin(mSmallestVisibleDL,smNumSkipLoadDetails);
|
|
if (skipDL < 0)
|
|
skipDL = 0;
|
|
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
if (TSShape::smReadVersion >= 27)
|
|
{
|
|
// Vertex format is set here
|
|
S8 *vboData = NULL;
|
|
S32 vboSize = 0;
|
|
|
|
mBasicVertexFormat.readAlloc(&tsalloc);
|
|
mVertexFormat.clear();
|
|
mBasicVertexFormat.getFormat(mVertexFormat);
|
|
mVertexSize = mVertexFormat.getSizeInBytes();
|
|
|
|
AssertFatal(mVertexSize == mBasicVertexFormat.vertexSize, "vertex size mismatch");
|
|
|
|
vboSize = tsalloc.get32();
|
|
vboData = tsalloc.getPointer8(vboSize);
|
|
|
|
if (tsalloc.getBuffer() && vboSize > 0)
|
|
{
|
|
U8 *vertexData = (U8*)dMalloc_aligned(vboSize, 16);
|
|
dMemcpy(vertexData, vboData, vboSize);
|
|
mShapeVertexData.set(vertexData, vboSize);
|
|
mShapeVertexData.vertexDataReady = true;
|
|
}
|
|
else
|
|
{
|
|
mShapeVertexData.set(NULL, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mShapeVertexData.set(NULL, 0);
|
|
}
|
|
|
|
// about to read in the meshes...first must allocate some scratch space
|
|
S32 scratchSize = getMax(numSkins,numMeshes);
|
|
TSMesh::smVertsList.setSize(scratchSize);
|
|
TSMesh::smTVertsList.setSize(scratchSize);
|
|
|
|
if ( smReadVersion >= 26 )
|
|
{
|
|
TSMesh::smTVerts2List.setSize(scratchSize);
|
|
TSMesh::smColorsList.setSize(scratchSize);
|
|
}
|
|
|
|
TSMesh::smNormsList.setSize(scratchSize);
|
|
TSMesh::smEncodedNormsList.setSize(scratchSize);
|
|
TSMesh::smDataCopied.setSize(scratchSize);
|
|
TSSkinMesh::smInitTransformList.setSize(scratchSize);
|
|
TSSkinMesh::smVertexIndexList.setSize(scratchSize);
|
|
TSSkinMesh::smBoneIndexList.setSize(scratchSize);
|
|
TSSkinMesh::smWeightList.setSize(scratchSize);
|
|
TSSkinMesh::smNodeIndexList.setSize(scratchSize);
|
|
for (i=0; i<numMeshes; i++)
|
|
{
|
|
TSMesh::smVertsList[i]=NULL;
|
|
TSMesh::smTVertsList[i]=NULL;
|
|
|
|
if ( smReadVersion >= 26 )
|
|
{
|
|
TSMesh::smTVerts2List[i] = NULL;
|
|
TSMesh::smColorsList[i] = NULL;
|
|
}
|
|
|
|
TSMesh::smNormsList[i]=NULL;
|
|
TSMesh::smEncodedNormsList[i]=NULL;
|
|
TSMesh::smDataCopied[i]=false;
|
|
TSSkinMesh::smInitTransformList[i] = NULL;
|
|
TSSkinMesh::smVertexIndexList[i] = NULL;
|
|
TSSkinMesh::smBoneIndexList[i] = NULL;
|
|
TSSkinMesh::smWeightList[i] = NULL;
|
|
TSSkinMesh::smNodeIndexList[i] = NULL;
|
|
}
|
|
|
|
// read in the meshes (sans skins)...straightforward read one at a time
|
|
TSMesh **ptrmesh = (TSMesh**)tsalloc.allocShape32((numMeshes + numSkins*numDetails) * (sizeof(TSMesh*) / 4));
|
|
S32 curObject = 0; // for tracking skipped meshes
|
|
for (i=0; i<numMeshes; i++)
|
|
{
|
|
bool skip = checkSkip(i,curObject,skipDL); // skip this mesh?
|
|
S32 meshType = tsalloc.get32();
|
|
if (meshType == TSMesh::DecalMeshType)
|
|
// decal mesh deprecated
|
|
skip = true;
|
|
TSMesh * mesh = TSMesh::assembleMesh(meshType,skip);
|
|
if (ptrmesh)
|
|
{
|
|
ptrmesh[i] = skip ? 0 : mesh;
|
|
}
|
|
|
|
// fill in location of verts, tverts, and normals for detail levels
|
|
if (mesh && meshType!=TSMesh::DecalMeshType)
|
|
{
|
|
TSMesh::smVertsList[i] = mesh->mVerts.address();
|
|
TSMesh::smTVertsList[i] = mesh->mTverts.address();
|
|
if (smReadVersion >= 26)
|
|
{
|
|
TSMesh::smTVerts2List[i] = mesh->mTverts2.address();
|
|
TSMesh::smColorsList[i] = mesh->mColors.address();
|
|
}
|
|
TSMesh::smNormsList[i] = mesh->mNorms.address();
|
|
TSMesh::smEncodedNormsList[i] = mesh->mEncodedNorms.address();
|
|
TSMesh::smDataCopied[i] = !skip; // as long as we didn't skip this mesh, the data should be in shape now
|
|
if (meshType==TSMesh::SkinMeshType)
|
|
{
|
|
TSSkinMesh * skin = (TSSkinMesh*)mesh;
|
|
TSMesh::smVertsList[i] = skin->batchData.initialVerts.address();
|
|
TSMesh::smNormsList[i] = skin->batchData.initialNorms.address();
|
|
TSSkinMesh::smInitTransformList[i] = skin->batchData.initialTransforms.address();
|
|
TSSkinMesh::smVertexIndexList[i] = skin->vertexIndex.address();
|
|
TSSkinMesh::smBoneIndexList[i] = skin->boneIndex.address();
|
|
TSSkinMesh::smWeightList[i] = skin->weight.address();
|
|
TSSkinMesh::smNodeIndexList[i] = skin->batchData.nodeIndex.address();
|
|
}
|
|
}
|
|
}
|
|
meshes.set(ptrmesh, numMeshes);
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
// names
|
|
char * nameBufferStart = (char*)tsalloc.getPointer8(0);
|
|
char * name = nameBufferStart;
|
|
S32 nameBufferSize = 0;
|
|
names.setSize(numNames);
|
|
for (i=0; i<numNames; i++)
|
|
{
|
|
for (j=0; name[j]; j++)
|
|
;
|
|
|
|
names[i] = name;
|
|
nameBufferSize += j + 1;
|
|
name += j + 1;
|
|
}
|
|
|
|
tsalloc.getPointer8(nameBufferSize);
|
|
tsalloc.align32();
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
if (smReadVersion<23)
|
|
{
|
|
// get detail information about skins...
|
|
S32 * detFirstSkin = tsalloc.getPointer32(numDetails);
|
|
S32 * detailNumSkins = tsalloc.getPointer32(numDetails);
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
// about to read in skins...clear out scratch space...
|
|
if (numSkins)
|
|
{
|
|
TSSkinMesh::smInitTransformList.setSize(numSkins);
|
|
TSSkinMesh::smVertexIndexList.setSize(numSkins);
|
|
TSSkinMesh::smBoneIndexList.setSize(numSkins);
|
|
TSSkinMesh::smWeightList.setSize(numSkins);
|
|
TSSkinMesh::smNodeIndexList.setSize(numSkins);
|
|
}
|
|
for (i=0; i<numSkins; i++)
|
|
{
|
|
TSMesh::smVertsList[i]=NULL;
|
|
TSMesh::smTVertsList[i]=NULL;
|
|
TSMesh::smNormsList[i]=NULL;
|
|
TSMesh::smEncodedNormsList[i]=NULL;
|
|
TSMesh::smDataCopied[i]=false;
|
|
TSSkinMesh::smInitTransformList[i] = NULL;
|
|
TSSkinMesh::smVertexIndexList[i] = NULL;
|
|
TSSkinMesh::smBoneIndexList[i] = NULL;
|
|
TSSkinMesh::smWeightList[i] = NULL;
|
|
TSSkinMesh::smNodeIndexList[i] = NULL;
|
|
}
|
|
|
|
// skins
|
|
ptr32 = tsalloc.allocShape32(numSkins);
|
|
for (i=0; i<numSkins; i++)
|
|
{
|
|
bool skip = i<detFirstSkin[skipDL];
|
|
TSSkinMesh * skin = (TSSkinMesh*)TSMesh::assembleMesh(TSMesh::SkinMeshType,skip);
|
|
if (meshes.address())
|
|
{
|
|
// add pointer to skin in shapes list of meshes
|
|
// we reserved room for this above...
|
|
meshes.set(meshes.address(),meshes.size()+1);
|
|
meshes[meshes.size()-1] = skip ? NULL : skin;
|
|
}
|
|
|
|
// fill in location of verts, tverts, and normals for shared detail levels
|
|
if (skin)
|
|
{
|
|
TSMesh::smVertsList[i] = skin->batchData.initialVerts.address();
|
|
TSMesh::smTVertsList[i] = skin->mTverts.address();
|
|
TSMesh::smNormsList[i] = skin->batchData.initialNorms.address();
|
|
TSMesh::smEncodedNormsList[i] = skin->mEncodedNorms.address();
|
|
TSMesh::smDataCopied[i] = !skip; // as long as we didn't skip this mesh, the data should be in shape now
|
|
TSSkinMesh::smInitTransformList[i] = skin->batchData.initialTransforms.address();
|
|
TSSkinMesh::smVertexIndexList[i] = skin->vertexIndex.address();
|
|
TSSkinMesh::smBoneIndexList[i] = skin->boneIndex.address();
|
|
TSSkinMesh::smWeightList[i] = skin->weight.address();
|
|
TSSkinMesh::smNodeIndexList[i] = skin->batchData.nodeIndex.address();
|
|
}
|
|
}
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
// we now have skins in mesh list...add skin objects to object list and patch things up
|
|
fixupOldSkins(numMeshes,numSkins,numDetails,detFirstSkin,detailNumSkins);
|
|
}
|
|
|
|
// allocate storage space for some arrays (filled in during Shape::init)...
|
|
ptr32 = tsalloc.allocShape32(numDetails);
|
|
alphaIn.set(ptr32,numDetails);
|
|
ptr32 = tsalloc.allocShape32(numDetails);
|
|
alphaOut.set(ptr32,numDetails);
|
|
}
|
|
|
|
void TSShape::disassembleShape()
|
|
{
|
|
S32 i;
|
|
|
|
// set counts...
|
|
S32 numNodes = tsalloc.set32(nodes.size());
|
|
S32 numObjects = tsalloc.set32(objects.size());
|
|
tsalloc.set32(0); // DEPRECATED decals
|
|
S32 numSubShapes = tsalloc.set32(subShapeFirstNode.size());
|
|
tsalloc.set32(0); // DEPRECATED ifl materials
|
|
S32 numNodeRotations = tsalloc.set32(nodeRotations.size());
|
|
S32 numNodeTranslations = tsalloc.set32(nodeTranslations.size());
|
|
S32 numNodeUniformScales = tsalloc.set32(nodeUniformScales.size());
|
|
S32 numNodeAlignedScales = tsalloc.set32(nodeAlignedScales.size());
|
|
S32 numNodeArbitraryScales = tsalloc.set32(nodeArbitraryScaleFactors.size());
|
|
S32 numGroundFrames = tsalloc.set32(groundTranslations.size());
|
|
S32 numObjectStates = tsalloc.set32(objectStates.size());
|
|
tsalloc.set32(0); // DEPRECATED decals
|
|
S32 numTriggers = tsalloc.set32(triggers.size());
|
|
S32 numDetails = tsalloc.set32(details.size());
|
|
S32 numMeshes = tsalloc.set32(meshes.size());
|
|
S32 numNames = tsalloc.set32(names.size());
|
|
tsalloc.set32((S32)mSmallestVisibleSize);
|
|
tsalloc.set32(mSmallestVisibleDL);
|
|
|
|
tsalloc.setGuard();
|
|
|
|
// get bounds...
|
|
tsalloc.copyToBuffer32((S32*)&mRadius,1);
|
|
tsalloc.copyToBuffer32((S32*)&tubeRadius,1);
|
|
tsalloc.copyToBuffer32((S32*)¢er,3);
|
|
tsalloc.copyToBuffer32((S32*)&mBounds,6);
|
|
|
|
tsalloc.setGuard();
|
|
|
|
// copy various vectors...
|
|
tsalloc.copyToBuffer32((S32*)nodes.address(),numNodes*5);
|
|
tsalloc.setGuard();
|
|
tsalloc.copyToBuffer32((S32*)objects.address(),numObjects*6);
|
|
tsalloc.setGuard();
|
|
// DEPRECATED: no copy decals
|
|
tsalloc.setGuard();
|
|
tsalloc.copyToBuffer32(0,0); // DEPRECATED: ifl materials!
|
|
tsalloc.setGuard();
|
|
tsalloc.copyToBuffer32((S32*)subShapeFirstNode.address(),numSubShapes);
|
|
tsalloc.copyToBuffer32((S32*)subShapeFirstObject.address(),numSubShapes);
|
|
tsalloc.copyToBuffer32(0, numSubShapes); // DEPRECATED: no copy subShapeFirstDecal
|
|
tsalloc.setGuard();
|
|
tsalloc.copyToBuffer32((S32*)subShapeNumNodes.address(),numSubShapes);
|
|
tsalloc.copyToBuffer32((S32*)subShapeNumObjects.address(),numSubShapes);
|
|
tsalloc.copyToBuffer32(0, numSubShapes); // DEPRECATED: no copy subShapeNumDecals
|
|
tsalloc.setGuard();
|
|
|
|
// default transforms...
|
|
tsalloc.copyToBuffer16((S16*)defaultRotations.address(),numNodes*4);
|
|
tsalloc.copyToBuffer32((S32*)defaultTranslations.address(),numNodes*3);
|
|
|
|
// animated transforms...
|
|
tsalloc.copyToBuffer16((S16*)nodeRotations.address(),numNodeRotations*4);
|
|
tsalloc.copyToBuffer32((S32*)nodeTranslations.address(),numNodeTranslations*3);
|
|
|
|
tsalloc.setGuard();
|
|
|
|
// ...with scale
|
|
tsalloc.copyToBuffer32((S32*)nodeUniformScales.address(),numNodeUniformScales);
|
|
tsalloc.copyToBuffer32((S32*)nodeAlignedScales.address(),numNodeAlignedScales*3);
|
|
tsalloc.copyToBuffer32((S32*)nodeArbitraryScaleFactors.address(),numNodeArbitraryScales*3);
|
|
tsalloc.copyToBuffer16((S16*)nodeArbitraryScaleRots.address(),numNodeArbitraryScales*4);
|
|
|
|
tsalloc.setGuard();
|
|
|
|
tsalloc.copyToBuffer32((S32*)groundTranslations.address(),3*numGroundFrames);
|
|
tsalloc.copyToBuffer16((S16*)groundRotations.address(),4*numGroundFrames);
|
|
|
|
tsalloc.setGuard();
|
|
|
|
// object states..
|
|
tsalloc.copyToBuffer32((S32*)objectStates.address(),numObjectStates*3);
|
|
tsalloc.setGuard();
|
|
|
|
// decal states...
|
|
// DEPRECATED (numDecalStates = 0)
|
|
tsalloc.setGuard();
|
|
|
|
// frame triggers
|
|
tsalloc.copyToBuffer32((S32*)triggers.address(),numTriggers*2);
|
|
tsalloc.setGuard();
|
|
|
|
// details
|
|
if (TSShape::smVersion > 25)
|
|
{
|
|
U32 alignedSize32 = sizeof( Detail ) / 4;
|
|
tsalloc.copyToBuffer32((S32*)details.address(),numDetails * alignedSize32 );
|
|
}
|
|
else
|
|
{
|
|
// Legacy details => no explicit autobillboard parameters
|
|
U32 legacyDetailSize32 = 7; // only store the first 7 4-byte values of each detail
|
|
for ( S32 bbID = 0; bbID < details.size(); bbID++ )
|
|
tsalloc.copyToBuffer32( (S32*)&details[bbID], legacyDetailSize32 );
|
|
}
|
|
tsalloc.setGuard();
|
|
|
|
if (TSShape::smVersion >= 27)
|
|
{
|
|
// Vertex format now included with mesh data. Note this doesn't include index data which
|
|
// is constructed directly in the buffer from the meshes
|
|
|
|
mBasicVertexFormat.writeAlloc(&tsalloc);
|
|
|
|
tsalloc.set32(mShapeVertexData.size);
|
|
tsalloc.copyToBuffer8((S8*)mShapeVertexData.base, mShapeVertexData.size);
|
|
}
|
|
|
|
// read in the meshes (sans skins)...
|
|
bool * isMesh = new bool[numMeshes]; // funny business because decals are pretend meshes (legacy issue)
|
|
for (i=0;i<numMeshes;i++)
|
|
isMesh[i]=false;
|
|
for (i=0; i<objects.size(); i++)
|
|
{
|
|
for (S32 j=0; j<objects[i].numMeshes; j++)
|
|
// even if an empty mesh, it's a mesh...
|
|
isMesh[objects[i].startMeshIndex+j]=true;
|
|
}
|
|
for (i=0; i<numMeshes; i++)
|
|
{
|
|
TSMesh * mesh = NULL;
|
|
// decal mesh deprecated
|
|
if (isMesh[i])
|
|
mesh = meshes[i];
|
|
tsalloc.set32( (mesh && mesh->getMeshType() != TSMesh::DecalMeshType) ? mesh->getMeshType() : TSMesh::NullMeshType);
|
|
if (mesh)
|
|
mesh->disassemble();
|
|
}
|
|
delete [] isMesh;
|
|
tsalloc.setGuard();
|
|
|
|
// names
|
|
for (i=0; i<numNames; i++)
|
|
tsalloc.copyToBuffer8((S8 *)(names[i].c_str()),names[i].length()+1);
|
|
|
|
tsalloc.setGuard();
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// write whole shape
|
|
//-------------------------------------------------
|
|
/** Determine whether we can write this shape in TSTPRO compatible format */
|
|
bool TSShape::canWriteOldFormat() const
|
|
{
|
|
// Cannot use old format if using autobillboard details
|
|
for (S32 i = 0; i < details.size(); i++)
|
|
{
|
|
if (details[i].subShapeNum < 0)
|
|
return false;
|
|
}
|
|
|
|
for (S32 i = 0; i < meshes.size(); i++)
|
|
{
|
|
if (!meshes[i])
|
|
continue;
|
|
|
|
// Cannot use old format if using the new functionality (COLORs, 2nd UV set)
|
|
if (meshes[i]->mTverts2.size() || meshes[i]->mColors.size())
|
|
return false;
|
|
|
|
// Cannot use old format if any primitive has too many triangles
|
|
// (ie. cannot fit in a S16)
|
|
for (S32 j = 0; j < meshes[i]->mPrimitives.size(); j++)
|
|
{
|
|
if ((meshes[i]->mPrimitives[j].start +
|
|
meshes[i]->mPrimitives[j].numElements) >= (1 << 15))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void TSShape::write(Stream * s, bool saveOldFormat)
|
|
{
|
|
S32 currentVersion = smVersion;
|
|
if (saveOldFormat)
|
|
smVersion = 24;
|
|
|
|
// write version
|
|
s->write(smVersion | (mExporterVersion<<16));
|
|
|
|
tsalloc.setWrite();
|
|
disassembleShape();
|
|
|
|
S32 * buffer32 = tsalloc.getBuffer32();
|
|
S16 * buffer16 = tsalloc.getBuffer16();
|
|
S8 * buffer8 = tsalloc.getBuffer8();
|
|
|
|
S32 size32 = tsalloc.getBufferSize32();
|
|
S32 size16 = tsalloc.getBufferSize16();
|
|
S32 size8 = tsalloc.getBufferSize8();
|
|
|
|
// convert sizes to dwords...
|
|
if (size16 & 1)
|
|
size16 += 2;
|
|
size16 >>= 1;
|
|
if (size8 & 3)
|
|
size8 += 4;
|
|
size8 >>= 2;
|
|
|
|
S32 sizeMemBuffer, start16, start8;
|
|
sizeMemBuffer = size32 + size16 + size8;
|
|
start16 = size32;
|
|
start8 = start16+size16;
|
|
|
|
// in dwords -- write will properly endian-flip.
|
|
s->write(sizeMemBuffer);
|
|
s->write(start16);
|
|
s->write(start8);
|
|
|
|
// endian-flip the entire write buffers.
|
|
fixEndian(buffer32,buffer16,buffer8,size32,size16,size8);
|
|
|
|
// now write buffers
|
|
s->write(size32*4,buffer32);
|
|
s->write(size16*4,buffer16);
|
|
s->write(size8 *4,buffer8);
|
|
|
|
// write sequences - write will properly endian-flip.
|
|
s->write(sequences.size());
|
|
for (S32 i=0; i<sequences.size(); i++)
|
|
sequences[i].write(s);
|
|
|
|
// write material list - write will properly endian-flip.
|
|
materialList->write(*s);
|
|
|
|
delete [] buffer32;
|
|
delete [] buffer16;
|
|
delete [] buffer8;
|
|
|
|
smVersion = currentVersion;
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// read whole shape
|
|
//-------------------------------------------------
|
|
|
|
bool TSShape::read(Stream * s)
|
|
{
|
|
// read version - read handles endian-flip
|
|
s->read(&smReadVersion);
|
|
mExporterVersion = smReadVersion >> 16;
|
|
smReadVersion &= 0xFF;
|
|
if (smReadVersion>smVersion)
|
|
{
|
|
// error -- don't support future versions yet :>
|
|
Con::errorf(ConsoleLogEntry::General,
|
|
"Error: attempt to load a version %i dts-shape, can currently only load version %i and before.",
|
|
smReadVersion,smVersion);
|
|
return false;
|
|
}
|
|
mReadVersion = smReadVersion;
|
|
|
|
S32 * memBuffer32;
|
|
S16 * memBuffer16;
|
|
S8 * memBuffer8;
|
|
S32 count32, count16, count8;
|
|
if (mReadVersion<19)
|
|
{
|
|
Con::errorf("... Shape with old version.");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
S32 i;
|
|
U32 sizeMemBuffer, startU16, startU8;
|
|
|
|
// in dwords. - read handles endian-flip
|
|
s->read(&sizeMemBuffer);
|
|
s->read(&startU16);
|
|
s->read(&startU8);
|
|
|
|
if (s->getStatus()!=Stream::Ok)
|
|
{
|
|
Con::errorf(ConsoleLogEntry::General, "Error: bad shape file.");
|
|
return false;
|
|
}
|
|
|
|
S32 * tmp = new S32[sizeMemBuffer];
|
|
s->read(sizeof(S32)*sizeMemBuffer,(U8*)tmp);
|
|
memBuffer32 = tmp;
|
|
memBuffer16 = (S16*)(tmp+startU16);
|
|
memBuffer8 = (S8*)(tmp+startU8);
|
|
|
|
count32 = startU16;
|
|
count16 = startU8-startU16;
|
|
count8 = sizeMemBuffer-startU8;
|
|
|
|
// read sequences
|
|
S32 numSequences;
|
|
s->read(&numSequences);
|
|
sequences.setSize(numSequences);
|
|
for (i=0; i<numSequences; i++)
|
|
{
|
|
sequences[i].read(s);
|
|
|
|
// Store initial (empty) source data
|
|
sequences[i].sourceData.total = sequences[i].numKeyframes;
|
|
sequences[i].sourceData.end = sequences[i].sourceData.total - 1;
|
|
}
|
|
|
|
// read material list
|
|
delete materialList; // just in case...
|
|
materialList = new TSMaterialList;
|
|
materialList->read(*s);
|
|
}
|
|
|
|
// since we read in the buffers, we need to endian-flip their entire contents...
|
|
fixEndian(memBuffer32,memBuffer16,memBuffer8,count32,count16,count8);
|
|
|
|
tsalloc.setRead(memBuffer32,memBuffer16,memBuffer8,true);
|
|
assembleShape(); // determine size of buffer needed
|
|
mShapeDataSize = tsalloc.getSize();
|
|
tsalloc.doAlloc();
|
|
mShapeData = tsalloc.getBuffer();
|
|
tsalloc.setRead(memBuffer32,memBuffer16,memBuffer8,false);
|
|
assembleShape(); // copy to buffer
|
|
AssertFatal(tsalloc.getSize()==mShapeDataSize,"TSShape::read: shape data buffer size mis-calculated");
|
|
|
|
delete [] memBuffer32;
|
|
|
|
if (smInitOnRead)
|
|
{
|
|
init();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void TSShape::createEmptyShape()
|
|
{
|
|
nodes.set(dMalloc(1 * sizeof(Node)), 1);
|
|
nodes[0].nameIndex = 1;
|
|
nodes[0].parentIndex = -1;
|
|
nodes[0].firstObject = 0;
|
|
nodes[0].firstChild = -1;
|
|
nodes[0].nextSibling = -1;
|
|
|
|
objects.set(dMalloc(1 * sizeof(Object)), 1);
|
|
objects[0].nameIndex = 2;
|
|
objects[0].numMeshes = 1;
|
|
objects[0].startMeshIndex = 0;
|
|
objects[0].nodeIndex = 0;
|
|
objects[0].nextSibling = -1;
|
|
objects[0].firstDecal = -1;
|
|
|
|
objectStates.set(dMalloc(1 * sizeof(ObjectState)), 1);
|
|
objectStates[0].vis = 1;
|
|
objectStates[0].frameIndex = 0;
|
|
objectStates[0].matFrameIndex = 0;
|
|
|
|
subShapeFirstNode.set(dMalloc(1 * sizeof(S32)), 1);
|
|
subShapeFirstNode[0] = 0;
|
|
|
|
subShapeFirstObject.set(dMalloc(1 * sizeof(S32)), 1);
|
|
subShapeFirstObject[0] = 0;
|
|
|
|
detailFirstSkin.set(NULL, 0);
|
|
|
|
subShapeNumNodes.set(dMalloc(1 * sizeof(S32)), 1);
|
|
subShapeNumNodes[0] = 1;
|
|
|
|
subShapeNumObjects.set(dMalloc(1 * sizeof(S32)), 1);
|
|
subShapeNumObjects[0] = 1;
|
|
|
|
details.set(dMalloc(1 * sizeof(Detail)), 1);
|
|
details[0].nameIndex = 0;
|
|
details[0].subShapeNum = 0;
|
|
details[0].objectDetailNum = 0;
|
|
details[0].size = 2.0f;
|
|
details[0].averageError = -1.0f;
|
|
details[0].maxError = -1.0f;
|
|
details[0].polyCount = 0;
|
|
|
|
defaultRotations.set(dMalloc(1 * sizeof(Quat16)), 1);
|
|
defaultRotations[0].x = 0.0f;
|
|
defaultRotations[0].y = 0.0f;
|
|
defaultRotations[0].z = 0.0f;
|
|
defaultRotations[0].w = 0.0f;
|
|
|
|
defaultTranslations.set(dMalloc(1 * sizeof(Point3F)), 1);
|
|
defaultTranslations[0].set(0.0f, 0.0f, 0.0f);
|
|
|
|
subShapeFirstTranslucentObject.set(dMalloc(1 * sizeof(S32)), 1);
|
|
subShapeFirstTranslucentObject[0] = 1;
|
|
|
|
alphaIn.set(dMalloc(1 * sizeof(F32)), 1);
|
|
alphaIn[0] = 0;
|
|
|
|
alphaOut.set(dMalloc(1 * sizeof(F32)), 1);
|
|
alphaOut[0] = -1;
|
|
|
|
sequences.set(NULL, 0);
|
|
nodeRotations.set(NULL, 0);
|
|
nodeTranslations.set(NULL, 0);
|
|
nodeUniformScales.set(NULL, 0);
|
|
nodeAlignedScales.set(NULL, 0);
|
|
nodeArbitraryScaleRots.set(NULL, 0);
|
|
nodeArbitraryScaleFactors.set(NULL, 0);
|
|
groundRotations.set(NULL, 0);
|
|
groundTranslations.set(NULL, 0);
|
|
triggers.set(NULL, 0);
|
|
billboardDetails.set(NULL, 0);
|
|
|
|
names.setSize(3);
|
|
names[0] = StringTable->insert("Detail2");
|
|
names[1] = StringTable->insert("Mesh2");
|
|
names[2] = StringTable->insert("Mesh");
|
|
|
|
mRadius = 0.866025f;
|
|
tubeRadius = 0.707107f;
|
|
center.set(0.0f, 0.5f, 0.0f);
|
|
mBounds.minExtents.set(-0.5f, 0.0f, -0.5f);
|
|
mBounds.maxExtents.set(0.5f, 1.0f, 0.5f);
|
|
|
|
mExporterVersion = 124;
|
|
mSmallestVisibleSize = 2;
|
|
mSmallestVisibleDL = 0;
|
|
mReadVersion = 24;
|
|
mFlags = 0;
|
|
mSequencesConstructed = 0;
|
|
|
|
mUseDetailFromScreenError = false;
|
|
|
|
mDetailLevelLookup.setSize( 1 );
|
|
mDetailLevelLookup[0].set( -1, 0 );
|
|
|
|
// Init the collision accelerator array. Note that we don't compute the
|
|
// accelerators until the app requests them
|
|
detailCollisionAccelerators.setSize(details.size());
|
|
for (U32 i = 0; i < detailCollisionAccelerators.size(); i++)
|
|
detailCollisionAccelerators[i] = NULL;
|
|
}
|
|
|
|
void TSShape::fixEndian(S32 * buff32, S16 * buff16, S8 *, S32 count32, S32 count16, S32)
|
|
{
|
|
// if endian-ness isn't the same, need to flip the buffer contents.
|
|
if (0x12345678!=convertLEndianToHost(0x12345678))
|
|
{
|
|
for (S32 i=0; i<count32; i++)
|
|
buff32[i]=convertLEndianToHost(buff32[i]);
|
|
for (S32 i=0; i<count16*2; i++)
|
|
buff16[i]=convertLEndianToHost(buff16[i]);
|
|
}
|
|
}
|
|
|
|
template<> void *Resource<TSShape>::create(const Torque::Path &path)
|
|
{
|
|
// Execute the shape script if it exists
|
|
Torque::Path scriptPath(path);
|
|
scriptPath.setExtension("cs");
|
|
|
|
// Don't execute the script if we're already doing so!
|
|
StringTableEntry currentScript = Platform::stripBasePath(CodeBlock::getCurrentCodeBlockFullPath());
|
|
if (!scriptPath.getFullPath().equal(currentScript))
|
|
{
|
|
Torque::Path scriptPathDSO(scriptPath);
|
|
scriptPathDSO.setExtension("cs.dso");
|
|
|
|
if (Torque::FS::IsFile(scriptPathDSO) || Torque::FS::IsFile(scriptPath))
|
|
{
|
|
String evalCmd = "exec(\"" + scriptPath + "\");";
|
|
|
|
String instantGroup = Con::getVariable("InstantGroup");
|
|
Con::setIntVariable("InstantGroup", RootGroupId);
|
|
Con::evaluate((const char*)evalCmd.c_str(), false, scriptPath.getFullPath());
|
|
Con::setVariable("InstantGroup", instantGroup.c_str());
|
|
}
|
|
}
|
|
|
|
// Attempt to load the shape
|
|
TSShape * ret = 0;
|
|
bool readSuccess = false;
|
|
const String extension = path.getExtension();
|
|
|
|
if ( extension.equal( "dts", String::NoCase ) )
|
|
{
|
|
FileStream stream;
|
|
stream.open( path.getFullPath(), Torque::FS::File::Read );
|
|
if ( stream.getStatus() != Stream::Ok )
|
|
{
|
|
Con::errorf( "Resource<TSShape>::create - Could not open '%s'", path.getFullPath().c_str() );
|
|
return NULL;
|
|
}
|
|
|
|
ret = new TSShape;
|
|
readSuccess = ret->read(&stream);
|
|
}
|
|
/*else if ( extension.equal( "dae", String::NoCase ) || extension.equal( "kmz", String::NoCase ) )
|
|
{
|
|
#ifdef TORQUE_COLLADA
|
|
// Attempt to load the DAE file
|
|
ret = loadColladaShape(path);
|
|
readSuccess = (ret != NULL);
|
|
#else
|
|
// No COLLADA support => attempt to load the cached DTS file instead
|
|
Torque::Path cachedPath = path;
|
|
cachedPath.setExtension("cached.dts");
|
|
|
|
FileStream stream;
|
|
stream.open( cachedPath.getFullPath(), Torque::FS::File::Read );
|
|
if ( stream.getStatus() != Stream::Ok )
|
|
{
|
|
Con::errorf( "Resource<TSShape>::create - Could not open '%s'", cachedPath.getFullPath().c_str() );
|
|
return NULL;
|
|
}
|
|
ret = new TSShape;
|
|
readSuccess = ret->read(&stream);
|
|
#endif
|
|
}*/
|
|
else
|
|
{
|
|
//Con::errorf( "Resource<TSShape>::create - '%s' has an unknown file format", path.getFullPath().c_str() );
|
|
//delete ret;
|
|
//return NULL;
|
|
|
|
// andrewmac: Open Asset Import Library
|
|
#ifdef TORQUE_ASSIMP
|
|
ret = assimpLoadShape(path);
|
|
readSuccess = (ret != NULL);
|
|
#endif
|
|
|
|
// andrewmac : I could have used another conditional macro but I think this is suffice:
|
|
if (!readSuccess)
|
|
{
|
|
Con::errorf("Resource<TSShape>::create - '%s' has an unknown file format", path.getFullPath().c_str());
|
|
delete ret;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if( !readSuccess )
|
|
{
|
|
Con::errorf( "Resource<TSShape>::create - Error reading '%s'", path.getFullPath().c_str() );
|
|
delete ret;
|
|
ret = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
template<> ResourceBase::Signature Resource<TSShape>::signature()
|
|
{
|
|
return MakeFourCC('t','s','s','h');
|
|
}
|
|
|
|
TSShape::ConvexHullAccelerator* TSShape::getAccelerator(S32 dl)
|
|
{
|
|
AssertFatal(dl < details.size(), "Error, bad detail level!");
|
|
if (dl == -1)
|
|
return NULL;
|
|
|
|
AssertFatal( detailCollisionAccelerators.size() == details.size(),
|
|
"TSShape::getAccelerator() - mismatched array sizes!" );
|
|
|
|
if (detailCollisionAccelerators[dl] == NULL)
|
|
computeAccelerator(dl);
|
|
|
|
AssertFatal(detailCollisionAccelerators[dl] != NULL, "This should be non-null after computing it!");
|
|
return detailCollisionAccelerators[dl];
|
|
}
|
|
|
|
|
|
void TSShape::computeAccelerator(S32 dl)
|
|
{
|
|
AssertFatal(dl < details.size(), "Error, bad detail level!");
|
|
|
|
// Have we already computed this?
|
|
if (detailCollisionAccelerators[dl] != NULL)
|
|
return;
|
|
|
|
// Create a bogus features list...
|
|
ConvexFeature cf;
|
|
MatrixF mat(true);
|
|
Point3F n(0, 0, 1);
|
|
|
|
const TSDetail* detail = &details[dl];
|
|
S32 ss = detail->subShapeNum;
|
|
S32 od = detail->objectDetailNum;
|
|
|
|
S32 start = subShapeFirstObject[ss];
|
|
S32 end = subShapeNumObjects[ss] + start;
|
|
if (start < end)
|
|
{
|
|
// run through objects and collide
|
|
// DMMNOTE: This assumes that the transform of the collision hulls is
|
|
// identity...
|
|
U32 surfaceKey = 0;
|
|
for (S32 i = start; i < end; i++)
|
|
{
|
|
const TSObject* obj = &objects[i];
|
|
|
|
if (obj->numMeshes && od < obj->numMeshes) {
|
|
TSMesh* mesh = meshes[obj->startMeshIndex + od];
|
|
if (mesh)
|
|
mesh->getFeatures(0, mat, n, &cf, surfaceKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector<Point3F> fixedVerts;
|
|
VECTOR_SET_ASSOCIATION(fixedVerts);
|
|
S32 i;
|
|
for (i = 0; i < cf.mVertexList.size(); i++) {
|
|
S32 j;
|
|
bool found = false;
|
|
for (j = 0; j < cf.mFaceList.size(); j++) {
|
|
if (cf.mFaceList[j].vertex[0] == i ||
|
|
cf.mFaceList[j].vertex[1] == i ||
|
|
cf.mFaceList[j].vertex[2] == i) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
continue;
|
|
|
|
found = false;
|
|
for (j = 0; j < fixedVerts.size(); j++) {
|
|
if (fixedVerts[j] == cf.mVertexList[i]) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found == true) {
|
|
// Ok, need to replace any references to vertex i in the facelists with
|
|
// a reference to vertex j in the fixed list
|
|
for (S32 k = 0; k < cf.mFaceList.size(); k++) {
|
|
for (S32 l = 0; l < 3; l++) {
|
|
if (cf.mFaceList[k].vertex[l] == i)
|
|
cf.mFaceList[k].vertex[l] = j;
|
|
}
|
|
}
|
|
} else {
|
|
for (S32 k = 0; k < cf.mFaceList.size(); k++) {
|
|
for (S32 l = 0; l < 3; l++) {
|
|
if (cf.mFaceList[k].vertex[l] == i)
|
|
cf.mFaceList[k].vertex[l] = fixedVerts.size();
|
|
}
|
|
}
|
|
fixedVerts.push_back(cf.mVertexList[i]);
|
|
}
|
|
}
|
|
cf.mVertexList.setSize(0);
|
|
cf.mVertexList = fixedVerts;
|
|
|
|
// Ok, so now we have a vertex list. Lets copy that out...
|
|
ConvexHullAccelerator* accel = new ConvexHullAccelerator;
|
|
detailCollisionAccelerators[dl] = accel;
|
|
accel->numVerts = cf.mVertexList.size();
|
|
accel->vertexList = new Point3F[accel->numVerts];
|
|
dMemcpy(accel->vertexList, cf.mVertexList.address(), sizeof(Point3F) * accel->numVerts);
|
|
|
|
accel->normalList = new Point3F[cf.mFaceList.size()];
|
|
for (i = 0; i < cf.mFaceList.size(); i++)
|
|
accel->normalList[i] = cf.mFaceList[i].normal;
|
|
|
|
accel->emitStrings = new U8*[accel->numVerts];
|
|
dMemset(accel->emitStrings, 0, sizeof(U8*) * accel->numVerts);
|
|
|
|
for (i = 0; i < accel->numVerts; i++) {
|
|
S32 j;
|
|
|
|
Vector<U32> faces;
|
|
VECTOR_SET_ASSOCIATION(faces);
|
|
for (j = 0; j < cf.mFaceList.size(); j++) {
|
|
if (cf.mFaceList[j].vertex[0] == i ||
|
|
cf.mFaceList[j].vertex[1] == i ||
|
|
cf.mFaceList[j].vertex[2] == i) {
|
|
faces.push_back(j);
|
|
}
|
|
}
|
|
AssertFatal(faces.size() != 0, "Huh? Vertex unreferenced by any faces");
|
|
|
|
// Insert all faces that didn't make the first cut, but share a plane with
|
|
// a face that's on the short list.
|
|
for (j = 0; j < cf.mFaceList.size(); j++) {
|
|
bool found = false;
|
|
S32 k;
|
|
for (k = 0; k < faces.size(); k++) {
|
|
if (faces[k] == j)
|
|
found = true;
|
|
}
|
|
if (found)
|
|
continue;
|
|
|
|
found = false;
|
|
for (k = 0; k < faces.size(); k++) {
|
|
if (mDot(accel->normalList[faces[k]], accel->normalList[j]) > 0.999) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found)
|
|
faces.push_back(j);
|
|
}
|
|
|
|
Vector<U32> vertRemaps;
|
|
VECTOR_SET_ASSOCIATION(vertRemaps);
|
|
for (j = 0; j < faces.size(); j++) {
|
|
for (U32 k = 0; k < 3; k++) {
|
|
U32 insert = cf.mFaceList[faces[j]].vertex[k];
|
|
bool found = false;
|
|
for (S32 l = 0; l < vertRemaps.size(); l++) {
|
|
if (insert == vertRemaps[l]) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
vertRemaps.push_back(insert);
|
|
}
|
|
}
|
|
|
|
Vector<Point2I> edges;
|
|
VECTOR_SET_ASSOCIATION(edges);
|
|
for (j = 0; j < faces.size(); j++) {
|
|
for (U32 k = 0; k < 3; k++) {
|
|
U32 edgeStart = cf.mFaceList[faces[j]].vertex[(k + 0) % 3];
|
|
U32 edgeEnd = cf.mFaceList[faces[j]].vertex[(k + 1) % 3];
|
|
|
|
U32 e0 = getMin(edgeStart, edgeEnd);
|
|
U32 e1 = getMax(edgeStart, edgeEnd);
|
|
|
|
bool found = false;
|
|
for (S32 l = 0; l < edges.size(); l++) {
|
|
if (edges[l].x == e0 && edges[l].y == e1) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
edges.push_back(Point2I(e0, e1));
|
|
}
|
|
}
|
|
|
|
//AssertFatal(vertRemaps.size() < 256 && faces.size() < 256 && edges.size() < 256,
|
|
// "Error, ran over the shapebase assumptions about convex hulls.");
|
|
|
|
U32 emitStringLen = 1 + vertRemaps.size() +
|
|
1 + (edges.size() * 2) +
|
|
1 + (faces.size() * 4);
|
|
accel->emitStrings[i] = new U8[emitStringLen];
|
|
|
|
U32 currPos = 0;
|
|
|
|
accel->emitStrings[i][currPos++] = vertRemaps.size();
|
|
for (j = 0; j < vertRemaps.size(); j++)
|
|
accel->emitStrings[i][currPos++] = vertRemaps[j];
|
|
|
|
accel->emitStrings[i][currPos++] = edges.size();
|
|
for (j = 0; j < edges.size(); j++) {
|
|
S32 l;
|
|
U32 old = edges[j].x;
|
|
bool found = false;
|
|
for (l = 0; l < vertRemaps.size(); l++) {
|
|
if (vertRemaps[l] == old) {
|
|
found = true;
|
|
accel->emitStrings[i][currPos++] = l;
|
|
break;
|
|
}
|
|
}
|
|
AssertFatal(found, "Error, couldn't find the remap!");
|
|
|
|
old = edges[j].y;
|
|
found = false;
|
|
for (l = 0; l < vertRemaps.size(); l++) {
|
|
if (vertRemaps[l] == old) {
|
|
found = true;
|
|
accel->emitStrings[i][currPos++] = l;
|
|
break;
|
|
}
|
|
}
|
|
AssertFatal(found, "Error, couldn't find the remap!");
|
|
}
|
|
|
|
accel->emitStrings[i][currPos++] = faces.size();
|
|
for (j = 0; j < faces.size(); j++) {
|
|
accel->emitStrings[i][currPos++] = faces[j];
|
|
for (U32 k = 0; k < 3; k++) {
|
|
U32 old = cf.mFaceList[faces[j]].vertex[k];
|
|
bool found = false;
|
|
for (S32 l = 0; l < vertRemaps.size(); l++) {
|
|
if (vertRemaps[l] == old) {
|
|
found = true;
|
|
accel->emitStrings[i][currPos++] = l;
|
|
break;
|
|
}
|
|
}
|
|
AssertFatal(found, "Error, couldn't find the remap!");
|
|
}
|
|
}
|
|
AssertFatal(currPos == emitStringLen, "Error, over/underflowed the emission string!");
|
|
}
|
|
}
|
|
|
|
void TSShape::finalizeEditable()
|
|
{
|
|
for (U32 i = 0; i < meshes.size(); i++)
|
|
{
|
|
if (meshes[i])
|
|
{
|
|
meshes[i]->clearEditable();
|
|
}
|
|
}
|
|
}
|