mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-11 14:44:36 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
49
Engine/source/ts/arch/tsMeshIntrinsics.arch.h
Normal file
49
Engine/source/ts/arch/tsMeshIntrinsics.arch.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSMESHINTRINSICS_ARCH_H_
|
||||
#define _TSMESHINTRINSICS_ARCH_H_
|
||||
|
||||
#if defined(TORQUE_CPU_X86)
|
||||
# // x86 CPU family implementations
|
||||
extern void zero_vert_normal_bulk_SSE(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride);
|
||||
extern void m_matF_x_BatchedVertWeightList_SSE(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride);
|
||||
#if (_MSC_VER >= 1500)
|
||||
extern void m_matF_x_BatchedVertWeightList_SSE4(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride);
|
||||
#endif
|
||||
#
|
||||
#elif defined(TORQUE_CPU_PPC)
|
||||
# // PPC CPU family implementations
|
||||
# if defined(TORQUE_OS_XENON)
|
||||
extern void zero_vert_normal_bulk_X360(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride);
|
||||
extern void m_matF_x_BatchedVertWeightList_X360(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride);
|
||||
# else
|
||||
extern void zero_vert_normal_bulk_gccvec(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride);
|
||||
extern void m_matF_x_BatchedVertWeightList_gccvec(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride);
|
||||
# endif
|
||||
#
|
||||
#else
|
||||
# // Other CPU types go here...
|
||||
#endif
|
||||
|
||||
#endif // _TSMESHINTRINSICS_ARCH_H_
|
||||
|
||||
183
Engine/source/ts/arch/tsMeshIntrinsics.sse.cpp
Normal file
183
Engine/source/ts/arch/tsMeshIntrinsics.sse.cpp
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "ts/tsMesh.h"
|
||||
|
||||
#if defined(TORQUE_CPU_X86)
|
||||
#include "ts/tsMeshIntrinsics.h"
|
||||
#include <xmmintrin.h>
|
||||
|
||||
void zero_vert_normal_bulk_SSE(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride)
|
||||
{
|
||||
// A U8 * version of the in/out pointer
|
||||
register char *outData = reinterpret_cast<char *>(outPtr);
|
||||
|
||||
register __m128 vPos;
|
||||
register __m128 vNrm;
|
||||
register __m128 vMask;
|
||||
|
||||
const __m128 _point3f_zero_mask = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
vMask = _mm_load_ps((const F32*)&_point3f_zero_mask);
|
||||
|
||||
// pre-populate cache
|
||||
for(int i = 0; i < 8; i++)
|
||||
_mm_prefetch(reinterpret_cast<const char *>(outData + outStride * i), _MM_HINT_T0);
|
||||
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
TSMesh::__TSMeshVertexBase *curElem = reinterpret_cast<TSMesh::__TSMeshVertexBase *>(outData);
|
||||
|
||||
// prefetch 8 items ahead (should really detect cache size or something)
|
||||
_mm_prefetch(reinterpret_cast<const char *>(outData + outStride * 8), _MM_HINT_T0);
|
||||
|
||||
// load
|
||||
vPos = _mm_load_ps(curElem->_vert);
|
||||
vNrm = _mm_load_ps(curElem->_normal);
|
||||
|
||||
// mask
|
||||
vPos = _mm_mul_ps(vPos, _point3f_zero_mask);
|
||||
vNrm = _mm_mul_ps(vNrm, _point3f_zero_mask);
|
||||
|
||||
// store
|
||||
_mm_store_ps(curElem->_vert, vPos);
|
||||
_mm_store_ps(curElem->_normal, vNrm);
|
||||
|
||||
// update output pointer
|
||||
outData += outStride;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void m_matF_x_BatchedVertWeightList_SSE(const MatrixF &mat,
|
||||
const dsize_t count,
|
||||
const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch,
|
||||
U8 * const __restrict outPtr,
|
||||
const dsize_t outStride)
|
||||
{
|
||||
const char * __restrict iPtr = reinterpret_cast<const char *>(batch);
|
||||
const dsize_t inStride = sizeof(TSSkinMesh::BatchData::BatchedVertWeight);
|
||||
|
||||
// SSE intrinsic version
|
||||
// Based on: http://www.cortstratton.org/articles/HugiCode.html
|
||||
|
||||
// Load matrix, transposed, into registers
|
||||
MatrixF transMat;
|
||||
mat.transposeTo(transMat);
|
||||
register __m128 sseMat[4];
|
||||
|
||||
sseMat[0] = _mm_loadu_ps(&transMat[0]);
|
||||
sseMat[1] = _mm_loadu_ps(&transMat[4]);
|
||||
sseMat[2] = _mm_loadu_ps(&transMat[8]);
|
||||
sseMat[3] = _mm_loadu_ps(&transMat[12]);
|
||||
|
||||
// mask
|
||||
const __m128 _w_mask = { 1.0f, 1.0f, 1.0f, 0.0f };
|
||||
|
||||
// temp registers
|
||||
register __m128 tempPos;
|
||||
register __m128 tempNrm;
|
||||
register __m128 scratch0;
|
||||
register __m128 scratch1;
|
||||
register __m128 inPos;
|
||||
register __m128 inNrm;
|
||||
|
||||
// pre-populate cache
|
||||
const TSSkinMesh::BatchData::BatchedVertWeight &firstElem = batch[0];
|
||||
for(int i = 0; i < 8; i++)
|
||||
{
|
||||
_mm_prefetch(reinterpret_cast<const char *>(iPtr + inStride * i), _MM_HINT_T0);
|
||||
_mm_prefetch(reinterpret_cast<const char *>(outPtr + outStride * (i + firstElem.vidx)), _MM_HINT_T0);
|
||||
}
|
||||
|
||||
for(register int i = 0; i < count; i++)
|
||||
{
|
||||
const TSSkinMesh::BatchData::BatchedVertWeight &inElem = batch[i];
|
||||
TSMesh::__TSMeshVertexBase *outElem = reinterpret_cast<TSMesh::__TSMeshVertexBase *>(outPtr + inElem.vidx * outStride);
|
||||
|
||||
// process x (hiding the prefetches in the delays)
|
||||
inPos = _mm_load_ps(inElem.vert);
|
||||
inNrm = _mm_load_ps(inElem.normal);
|
||||
|
||||
// prefetch input
|
||||
#define INPUT_PREFETCH_LOOKAHEAD 64
|
||||
const char *prefetchInput = reinterpret_cast<const char *>(batch) + inStride * (i + INPUT_PREFETCH_LOOKAHEAD);
|
||||
_mm_prefetch(prefetchInput, _MM_HINT_T0);
|
||||
|
||||
// propagate the .x elements across the vectors
|
||||
tempPos = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
tempNrm = _mm_shuffle_ps(inNrm, inNrm, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
|
||||
// prefetch ouput with half the lookahead distance of the input
|
||||
#define OUTPUT_PREFETCH_LOOKAHEAD (INPUT_PREFETCH_LOOKAHEAD >> 1)
|
||||
const char *outPrefetch = reinterpret_cast<const char*>(outPtr) + outStride * (inElem.vidx + OUTPUT_PREFETCH_LOOKAHEAD);
|
||||
_mm_prefetch(outPrefetch, _MM_HINT_T0);
|
||||
|
||||
// mul by column 0
|
||||
tempPos = _mm_mul_ps(tempPos, sseMat[0]);
|
||||
tempNrm = _mm_mul_ps(tempNrm, sseMat[0]);
|
||||
|
||||
// process y
|
||||
scratch0 = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
scratch1 = _mm_shuffle_ps(inNrm, inNrm, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
|
||||
scratch0 = _mm_mul_ps(scratch0, sseMat[1]);
|
||||
scratch1 = _mm_mul_ps(scratch1, sseMat[1]);
|
||||
|
||||
tempPos = _mm_add_ps(tempPos, scratch0);
|
||||
tempNrm = _mm_add_ps(tempNrm, scratch1);
|
||||
|
||||
|
||||
// process z
|
||||
scratch0 = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
scratch1 = _mm_shuffle_ps(inNrm, inNrm, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
|
||||
scratch0 = _mm_mul_ps(scratch0, sseMat[2]);
|
||||
scratch1 = _mm_mul_ps(scratch1, sseMat[2]);
|
||||
|
||||
tempPos = _mm_add_ps(tempPos, scratch0);
|
||||
|
||||
inNrm = _mm_load_ps(outElem->_normal); //< load normal for accumulation
|
||||
scratch0 = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(3, 3, 3, 3));//< load bone weight across all elements of scratch0
|
||||
|
||||
tempNrm = _mm_add_ps(tempNrm, scratch1);
|
||||
|
||||
scratch0 = _mm_mul_ps(scratch0, _w_mask); //< mask off last
|
||||
|
||||
// Translate the position by adding the 4th column of the matrix to it
|
||||
tempPos = _mm_add_ps(tempPos, sseMat[3]);
|
||||
|
||||
// now multiply by the blend weight, and mask out the W component of both vectors
|
||||
tempPos = _mm_mul_ps(tempPos, scratch0);
|
||||
tempNrm = _mm_mul_ps(tempNrm, scratch0);
|
||||
|
||||
inPos = _mm_load_ps(outElem->_vert); //< load position for accumulation
|
||||
|
||||
// accumulate with previous values
|
||||
tempNrm = _mm_add_ps(tempNrm, inNrm);
|
||||
tempPos = _mm_add_ps(tempPos, inPos);
|
||||
|
||||
_mm_store_ps(outElem->_vert, tempPos); //< output position
|
||||
_mm_store_ps(outElem->_normal, tempNrm); //< output normal
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TORQUE_CPU_X86
|
||||
108
Engine/source/ts/arch/tsMeshIntrinsics.sse4.cpp
Normal file
108
Engine/source/ts/arch/tsMeshIntrinsics.sse4.cpp
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "ts/tsMesh.h"
|
||||
|
||||
#if defined(TORQUE_CPU_X86) && (_MSC_VER >= 1500)
|
||||
#include "ts/tsMeshIntrinsics.h"
|
||||
#include <smmintrin.h>
|
||||
|
||||
void m_matF_x_BatchedVertWeightList_SSE4(const MatrixF &mat,
|
||||
const dsize_t count,
|
||||
const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch,
|
||||
U8 * const __restrict outPtr,
|
||||
const dsize_t outStride)
|
||||
{
|
||||
const char * __restrict iPtr = reinterpret_cast<const char *>(batch);
|
||||
const dsize_t inStride = sizeof(TSSkinMesh::BatchData::BatchedVertWeight);
|
||||
|
||||
__m128 sseMat[3];
|
||||
sseMat[0] = _mm_loadu_ps(&mat[0]);
|
||||
sseMat[1] = _mm_loadu_ps(&mat[4]);
|
||||
sseMat[2] = _mm_loadu_ps(&mat[8]);
|
||||
|
||||
// temp registers
|
||||
__m128 inPos, tempPos;
|
||||
__m128 inNrm, tempNrm;
|
||||
__m128 temp0, temp1, temp2, temp3;
|
||||
|
||||
// pre-populate cache
|
||||
const TSSkinMesh::BatchData::BatchedVertWeight &firstElem = batch[0];
|
||||
for(int i = 0; i < 8; i++)
|
||||
{
|
||||
_mm_prefetch(reinterpret_cast<const char *>(iPtr + inStride * i), _MM_HINT_T0);
|
||||
_mm_prefetch(reinterpret_cast<const char *>(outPtr + outStride * (i + firstElem.vidx)), _MM_HINT_T0);
|
||||
}
|
||||
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
const TSSkinMesh::BatchData::BatchedVertWeight &inElem = batch[i];
|
||||
TSMesh::__TSMeshVertexBase *outElem = reinterpret_cast<TSMesh::__TSMeshVertexBase *>(outPtr + inElem.vidx * outStride);
|
||||
|
||||
// process x (hiding the prefetches in the delays)
|
||||
inPos = _mm_load_ps(inElem.vert);
|
||||
inNrm = _mm_load_ps(inElem.normal);
|
||||
|
||||
// prefetch input
|
||||
#define INPUT_PREFETCH_LOOKAHEAD 64
|
||||
const char *prefetchInput = reinterpret_cast<const char *>(batch) + inStride * (i + INPUT_PREFETCH_LOOKAHEAD);
|
||||
_mm_prefetch(prefetchInput, _MM_HINT_T0);
|
||||
|
||||
// prefetch ouput with half the lookahead distance of the input
|
||||
#define OUTPUT_PREFETCH_LOOKAHEAD (INPUT_PREFETCH_LOOKAHEAD >> 1)
|
||||
const char *outPrefetch = reinterpret_cast<const char*>(outPtr) + outStride * (inElem.vidx + OUTPUT_PREFETCH_LOOKAHEAD);
|
||||
_mm_prefetch(outPrefetch, _MM_HINT_T0);
|
||||
|
||||
// Multiply position
|
||||
tempPos = _mm_dp_ps(inPos, sseMat[0], 0xF1);
|
||||
temp0 = _mm_dp_ps(inPos, sseMat[1], 0xF2);
|
||||
temp1 = _mm_dp_ps(inPos, sseMat[2], 0xF4);
|
||||
|
||||
temp0 = _mm_or_ps(temp0, temp1);
|
||||
tempPos = _mm_or_ps(tempPos, temp0);
|
||||
|
||||
// Multiply normal
|
||||
tempNrm = _mm_dp_ps(inNrm, sseMat[0], 0x71);
|
||||
temp2 = _mm_dp_ps(inNrm, sseMat[1], 0x72);
|
||||
temp3 = _mm_dp_ps(inNrm, sseMat[2], 0x74);
|
||||
|
||||
temp2 = _mm_or_ps(temp2, temp3);
|
||||
tempNrm = _mm_or_ps(tempNrm, temp2);
|
||||
|
||||
// Load bone weight and multiply
|
||||
temp3 = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
tempPos = _mm_mul_ps(tempPos, temp3);
|
||||
tempNrm = _mm_mul_ps(tempNrm, temp3);
|
||||
|
||||
inPos = _mm_load_ps(outElem->_vert); //< load position for accumulation
|
||||
inNrm = _mm_load_ps(outElem->_normal); //< load normal for accumulation
|
||||
|
||||
// accumulate with previous values
|
||||
tempNrm = _mm_add_ps(tempNrm, inNrm);
|
||||
tempPos = _mm_add_ps(tempPos, inPos);
|
||||
|
||||
_mm_store_ps(outElem->_vert, tempPos); //< output position
|
||||
_mm_store_ps(outElem->_normal, tempNrm); //< output normal
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TORQUE_CPU_X86
|
||||
225
Engine/source/ts/collada/colladaAppMaterial.cpp
Normal file
225
Engine/source/ts/collada/colladaAppMaterial.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/loader/tsShapeLoader.h"
|
||||
#include "ts/collada/colladaAppMaterial.h"
|
||||
#include "ts/collada/colladaUtils.h"
|
||||
#include "ts/tsMaterialList.h"
|
||||
#include "materials/materialManager.h"
|
||||
|
||||
using namespace ColladaUtils;
|
||||
|
||||
String cleanString(const String& str)
|
||||
{
|
||||
String cleanStr(str);
|
||||
|
||||
// Replace invalid characters with underscores
|
||||
const String badChars(" -,.+=*/");
|
||||
for (String::SizeType i = 0; i < badChars.length(); i++)
|
||||
cleanStr.replace(badChars[i], '_');
|
||||
|
||||
// Prefix with an underscore if string starts with a number
|
||||
if ((cleanStr[0] >= '0') && (cleanStr[0] <= '9'))
|
||||
cleanStr.insert(0, '_');
|
||||
|
||||
return cleanStr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ColladaAppMaterial::ColladaAppMaterial(const char* matName)
|
||||
: mat(0),
|
||||
effect(0),
|
||||
effectExt(0)
|
||||
{
|
||||
name = matName;
|
||||
|
||||
// Set some defaults
|
||||
flags |= TSMaterialList::S_Wrap;
|
||||
flags |= TSMaterialList::T_Wrap;
|
||||
|
||||
diffuseColor = ColorF::ONE;
|
||||
specularColor = ColorF::ONE;
|
||||
specularPower = 8.0f;
|
||||
doubleSided = false;
|
||||
}
|
||||
|
||||
ColladaAppMaterial::ColladaAppMaterial(const domMaterial *pMat)
|
||||
: mat(pMat),
|
||||
diffuseColor(ColorF::ONE),
|
||||
specularColor(ColorF::ONE),
|
||||
specularPower(8.0f),
|
||||
doubleSided(false)
|
||||
{
|
||||
// Get the effect element for this material
|
||||
effect = daeSafeCast<domEffect>(mat->getInstance_effect()->getUrl().getElement());
|
||||
effectExt = new ColladaExtension_effect(effect);
|
||||
|
||||
// Get the <profile_COMMON>, <diffuse> and <specular> elements
|
||||
const domProfile_COMMON* commonProfile = ColladaUtils::findEffectCommonProfile(effect);
|
||||
const domCommon_color_or_texture_type_complexType* domDiffuse = findEffectDiffuse(effect);
|
||||
const domCommon_color_or_texture_type_complexType* domSpecular = findEffectSpecular(effect);
|
||||
|
||||
// Wrap flags
|
||||
if (effectExt->wrapU)
|
||||
flags |= TSMaterialList::S_Wrap;
|
||||
if (effectExt->wrapV)
|
||||
flags |= TSMaterialList::T_Wrap;
|
||||
|
||||
// Set material attributes
|
||||
if (commonProfile) {
|
||||
|
||||
F32 transparency = 0.0f;
|
||||
if (commonProfile->getTechnique()->getConstant()) {
|
||||
const domProfile_COMMON::domTechnique::domConstant* constant = commonProfile->getTechnique()->getConstant();
|
||||
diffuseColor.set(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
resolveColor(constant->getReflective(), &specularColor);
|
||||
resolveFloat(constant->getReflectivity(), &specularPower);
|
||||
resolveTransparency(constant, &transparency);
|
||||
}
|
||||
else if (commonProfile->getTechnique()->getLambert()) {
|
||||
const domProfile_COMMON::domTechnique::domLambert* lambert = commonProfile->getTechnique()->getLambert();
|
||||
resolveColor(lambert->getDiffuse(), &diffuseColor);
|
||||
resolveColor(lambert->getReflective(), &specularColor);
|
||||
resolveFloat(lambert->getReflectivity(), &specularPower);
|
||||
resolveTransparency(lambert, &transparency);
|
||||
}
|
||||
else if (commonProfile->getTechnique()->getPhong()) {
|
||||
const domProfile_COMMON::domTechnique::domPhong* phong = commonProfile->getTechnique()->getPhong();
|
||||
resolveColor(phong->getDiffuse(), &diffuseColor);
|
||||
resolveColor(phong->getSpecular(), &specularColor);
|
||||
resolveFloat(phong->getShininess(), &specularPower);
|
||||
resolveTransparency(phong, &transparency);
|
||||
}
|
||||
else if (commonProfile->getTechnique()->getBlinn()) {
|
||||
const domProfile_COMMON::domTechnique::domBlinn* blinn = commonProfile->getTechnique()->getBlinn();
|
||||
resolveColor(blinn->getDiffuse(), &diffuseColor);
|
||||
resolveColor(blinn->getSpecular(), &specularColor);
|
||||
resolveFloat(blinn->getShininess(), &specularPower);
|
||||
resolveTransparency(blinn, &transparency);
|
||||
}
|
||||
|
||||
// Normalize specularPower (1-128). Values > 1 are assumed to be
|
||||
// already normalized.
|
||||
if (specularPower <= 1.0f)
|
||||
specularPower *= 128;
|
||||
specularPower = mClampF(specularPower, 1.0f, 128.0f);
|
||||
|
||||
// Set translucency
|
||||
if (transparency != 0.0f) {
|
||||
flags |= TSMaterialList::Translucent;
|
||||
if (transparency > 1.0f) {
|
||||
flags |= TSMaterialList::Additive;
|
||||
diffuseColor.alpha = transparency - 1.0f;
|
||||
}
|
||||
else if (transparency < 0.0f) {
|
||||
flags |= TSMaterialList::Subtractive;
|
||||
diffuseColor.alpha = -transparency;
|
||||
}
|
||||
else {
|
||||
diffuseColor.alpha = transparency;
|
||||
}
|
||||
}
|
||||
else
|
||||
diffuseColor.alpha = 1.0f;
|
||||
}
|
||||
|
||||
// Double-sided flag
|
||||
doubleSided = effectExt->double_sided;
|
||||
|
||||
// Get the paths for the various textures => Collada indirection at its finest!
|
||||
// <texture>.<newparam>.<sampler2D>.<source>.<newparam>.<surface>.<init_from>.<image>.<init_from>
|
||||
diffuseMap = getSamplerImagePath(effect, getTextureSampler(effect, domDiffuse));
|
||||
specularMap = getSamplerImagePath(effect, getTextureSampler(effect, domSpecular));
|
||||
normalMap = getSamplerImagePath(effect, effectExt->bumpSampler);
|
||||
|
||||
// Set the material name
|
||||
name = ColladaUtils::getOptions().matNamePrefix;
|
||||
if ( ColladaUtils::getOptions().useDiffuseNames )
|
||||
{
|
||||
Torque::Path diffusePath( diffuseMap );
|
||||
name += diffusePath.getFileName();
|
||||
}
|
||||
else
|
||||
{
|
||||
name += _GetNameOrId(mat);
|
||||
}
|
||||
}
|
||||
|
||||
void ColladaAppMaterial::resolveFloat(const domCommon_float_or_param_type* value, F32* dst)
|
||||
{
|
||||
if (value && value->getFloat()) {
|
||||
*dst = value->getFloat()->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
void ColladaAppMaterial::resolveColor(const domCommon_color_or_texture_type* value, ColorF* dst)
|
||||
{
|
||||
if (value && value->getColor()) {
|
||||
dst->red = value->getColor()->getValue()[0];
|
||||
dst->green = value->getColor()->getValue()[1];
|
||||
dst->blue = value->getColor()->getValue()[2];
|
||||
dst->alpha = value->getColor()->getValue()[3];
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a new Material object
|
||||
Material *ColladaAppMaterial::createMaterial(const Torque::Path& path) const
|
||||
{
|
||||
// The filename and material name are used as TorqueScript identifiers, so
|
||||
// clean them up first
|
||||
String cleanFile = cleanString(TSShapeLoader::getShapePath().getFileName());
|
||||
String cleanName = cleanString(getName());
|
||||
|
||||
// Prefix the material name with the filename (if not done already by TSShapeConstructor prefix)
|
||||
if (!cleanName.startsWith(cleanFile))
|
||||
cleanName = cleanFile + "_" + cleanName;
|
||||
|
||||
// Determine the blend operation for this material
|
||||
Material::BlendOp blendOp = (flags & TSMaterialList::Translucent) ? Material::LerpAlpha : Material::None;
|
||||
if (flags & TSMaterialList::Additive)
|
||||
blendOp = Material::Add;
|
||||
else if (flags & TSMaterialList::Subtractive)
|
||||
blendOp = Material::Sub;
|
||||
|
||||
// Create the Material definition
|
||||
const String oldScriptFile = Con::getVariable("$Con::File");
|
||||
Con::setVariable("$Con::File", path.getFullPath()); // modify current script path so texture lookups are correct
|
||||
Material *newMat = MATMGR->allocateAndRegister( cleanName, getName() );
|
||||
Con::setVariable("$Con::File", oldScriptFile); // restore script path
|
||||
|
||||
newMat->mDiffuseMapFilename[0] = diffuseMap;
|
||||
newMat->mNormalMapFilename[0] = normalMap;
|
||||
newMat->mSpecularMapFilename[0] = specularMap;
|
||||
|
||||
newMat->mDiffuse[0] = diffuseColor;
|
||||
newMat->mSpecular[0] = specularColor;
|
||||
newMat->mSpecularPower[0] = specularPower;
|
||||
|
||||
newMat->mDoubleSided = doubleSided;
|
||||
newMat->mTranslucent = (bool)(flags & TSMaterialList::Translucent);
|
||||
newMat->mTranslucentBlendOp = blendOp;
|
||||
|
||||
return newMat;
|
||||
}
|
||||
89
Engine/source/ts/collada/colladaAppMaterial.h
Normal file
89
Engine/source/ts/collada/colladaAppMaterial.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _COLLADA_APP_MATERIAL_H_
|
||||
#define _COLLADA_APP_MATERIAL_H_
|
||||
|
||||
#ifndef _APPMATERIAL_H_
|
||||
#include "ts/loader/appMaterial.h"
|
||||
#endif
|
||||
#ifndef _COLLADA_EXTENSIONS_H_
|
||||
#include "ts/collada/colladaExtensions.h"
|
||||
#endif
|
||||
|
||||
class Material;
|
||||
|
||||
class ColladaAppMaterial : public AppMaterial
|
||||
{
|
||||
public:
|
||||
const domMaterial* mat; ///< Collada <material> element
|
||||
domEffect* effect; ///< Collada <effect> element
|
||||
ColladaExtension_effect* effectExt; ///< effect extension
|
||||
String name; ///< Name of this material (cleaned)
|
||||
|
||||
// Settings extracted from the Collada file, and optionally saved to materials.cs
|
||||
String diffuseMap;
|
||||
String normalMap;
|
||||
String specularMap;
|
||||
ColorF diffuseColor;
|
||||
ColorF specularColor;
|
||||
F32 specularPower;
|
||||
bool doubleSided;
|
||||
|
||||
ColladaAppMaterial(const char* matName);
|
||||
ColladaAppMaterial(const domMaterial* pMat);
|
||||
~ColladaAppMaterial() { delete effectExt; }
|
||||
|
||||
String getName() const { return name; }
|
||||
|
||||
void resolveFloat(const domCommon_float_or_param_type* value, F32* dst);
|
||||
void resolveColor(const domCommon_color_or_texture_type* value, ColorF* dst);
|
||||
|
||||
// Determine the material transparency
|
||||
template<class T> void resolveTransparency(const T shader, F32* dst)
|
||||
{
|
||||
// Start out by getting the <transparency> value
|
||||
*dst = 1.0f;
|
||||
resolveFloat(shader->getTransparency(), dst);
|
||||
|
||||
// Multiply the transparency by the transparent color
|
||||
ColorF transColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
if (shader->getTransparent() && shader->getTransparent()->getColor()) {
|
||||
const domCommon_color_or_texture_type::domColor* color = shader->getTransparent()->getColor();
|
||||
transColor.set(color->getValue()[0], color->getValue()[1], color->getValue()[2], color->getValue()[3]);
|
||||
}
|
||||
|
||||
if (!shader->getTransparent() || (shader->getTransparent()->getOpaque() == FX_OPAQUE_ENUM_A_ONE)) {
|
||||
// multiply by alpha value and invert (so 1.0 is fully opaque)
|
||||
*dst = 1.0f - (*dst * transColor.alpha);
|
||||
}
|
||||
else {
|
||||
// multiply by average of the RGB values
|
||||
F32 avg = (transColor.red + transColor.blue + transColor.green) / 3;
|
||||
*dst *= avg;
|
||||
}
|
||||
}
|
||||
|
||||
Material *createMaterial(const Torque::Path& path) const;
|
||||
};
|
||||
|
||||
#endif // _COLLADA_APP_MATERIAL_H_
|
||||
1099
Engine/source/ts/collada/colladaAppMesh.cpp
Normal file
1099
Engine/source/ts/collada/colladaAppMesh.cpp
Normal file
File diff suppressed because it is too large
Load diff
232
Engine/source/ts/collada/colladaAppMesh.h
Normal file
232
Engine/source/ts/collada/colladaAppMesh.h
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _COLLADA_APPMESH_H_
|
||||
#define _COLLADA_APPMESH_H_
|
||||
|
||||
#ifndef _TDICTIONARY_H_
|
||||
#include "core/tDictionary.h"
|
||||
#endif
|
||||
#ifndef _APPMESH_H_
|
||||
#include "ts/loader/appMesh.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPELOADER_H_
|
||||
#include "ts/loader/tsShapeLoader.h"
|
||||
#endif
|
||||
#ifndef _COLLADA_APPNODE_H_
|
||||
#include "ts/collada/colladaAppNode.h"
|
||||
#endif
|
||||
#ifndef _COLLADA_EXTENSIONS_H_
|
||||
#include "ts/collada/colladaExtensions.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque unifies the vert position, normal and UV values, so that a single index
|
||||
// uniquely identifies all 3 elements. A triangle then contains just 3 indices,
|
||||
// and from that we can get the 3 positions, 3 normals and 3 UVs.
|
||||
//
|
||||
// for i=1:3
|
||||
// index = indices[triangle.start + i]
|
||||
// points[index], normals[index], uvs[index]
|
||||
//
|
||||
// Collada does not use unified vertex streams, (each triangle needs 9 indices),
|
||||
// so this structure is used to map a single VertTuple index to 3 indices into
|
||||
// the Collada streams. The Collada (and Torque) primitive index is also stored
|
||||
// because the Collada document may use different streams for different primitives.
|
||||
//
|
||||
// For morph geometry, we can use the same array of VertTuples to access the base
|
||||
// AND all of the target geometries because they MUST have the same topology.
|
||||
struct VertTuple
|
||||
{
|
||||
S32 prim, vertex, normal, color, uv, uv2;
|
||||
|
||||
Point3F dataVertex, dataNormal;
|
||||
ColorI dataColor;
|
||||
Point2F dataUV, dataUV2;
|
||||
|
||||
VertTuple(): prim(-1), vertex(-1), normal(-1), color(-1), uv(-1), uv2(-1) {}
|
||||
bool operator==(const VertTuple& p) const
|
||||
{
|
||||
return dataVertex == p.dataVertex &&
|
||||
dataColor == p.dataColor &&
|
||||
dataNormal == p.dataNormal &&
|
||||
dataUV == p.dataUV &&
|
||||
dataUV2 == p.dataUV2;
|
||||
}
|
||||
};
|
||||
|
||||
class ColladaAppMesh : public AppMesh
|
||||
{
|
||||
typedef AppMesh Parent;
|
||||
|
||||
protected:
|
||||
class ColladaAppNode* appNode; ///< Pointer to the node that owns this mesh
|
||||
const domInstance_geometry* instanceGeom;
|
||||
const domInstance_controller* instanceCtrl;
|
||||
ColladaExtension_geometry* geomExt; ///< geometry extension
|
||||
|
||||
Vector<VertTuple> vertTuples; ///<
|
||||
Map<StringTableEntry,U32> boundMaterials; ///< Local map of symbols to materials
|
||||
|
||||
static bool fixedSizeEnabled; ///< Set to true to fix the detail size to a particular value for all geometry
|
||||
static S32 fixedSize; ///< The fixed detail size value for all geometry
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/// Get the morph controller for this mesh (if any)
|
||||
const domMorph* getMorph()
|
||||
{
|
||||
if (instanceCtrl) {
|
||||
const domController* ctrl = daeSafeCast<domController>(instanceCtrl->getUrl().getElement());
|
||||
if (ctrl && ctrl->getSkin())
|
||||
ctrl = daeSafeCast<domController>(ctrl->getSkin()->getSource().getElement());
|
||||
return ctrl ? ctrl->getMorph() : NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
S32 addMaterial(const char* symbol);
|
||||
|
||||
bool checkGeometryType(const daeElement* element);
|
||||
void getPrimitives(const domGeometry* geometry);
|
||||
|
||||
void getVertexData( const domGeometry* geometry, F32 time, const MatrixF& objectOffset,
|
||||
Vector<Point3F>& points, Vector<Point3F>& norms, Vector<ColorI>& colors,
|
||||
Vector<Point2F>& uvs, Vector<Point2F>& uv2s, bool appendValues);
|
||||
|
||||
void getMorphVertexData( const domMorph* morph, F32 time, const MatrixF& objectOffset,
|
||||
Vector<Point3F>& points, Vector<Point3F>& norms, Vector<ColorI>& colors,
|
||||
Vector<Point2F>& uvs, Vector<Point2F>& uv2s );
|
||||
|
||||
public:
|
||||
|
||||
ColladaAppMesh(const domInstance_geometry* instance, ColladaAppNode* node);
|
||||
ColladaAppMesh(const domInstance_controller* instance, ColladaAppNode* node);
|
||||
~ColladaAppMesh()
|
||||
{
|
||||
delete geomExt;
|
||||
}
|
||||
|
||||
static void fixDetailSize(bool fixed, S32 size=2)
|
||||
{
|
||||
fixedSizeEnabled = fixed;
|
||||
fixedSize = size;
|
||||
}
|
||||
|
||||
/// Get the name of this mesh
|
||||
///
|
||||
/// @return A string containing the name of this mesh
|
||||
const char *getName(bool allowFixed=true);
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/// Get a floating point property value
|
||||
///
|
||||
/// @param propName Name of the property to get
|
||||
/// @param defaultVal Reference to variable to hold return value
|
||||
///
|
||||
/// @return True if a value was set, false if not
|
||||
bool getFloat(const char *propName, F32 &defaultVal)
|
||||
{
|
||||
return appNode->getFloat(propName,defaultVal);
|
||||
}
|
||||
|
||||
/// Get an integer property value
|
||||
///
|
||||
/// @param propName Name of the property to get
|
||||
/// @param defaultVal Reference to variable to hold return value
|
||||
///
|
||||
/// @return True if a value was set, false if not
|
||||
bool getInt(const char *propName, S32 &defaultVal)
|
||||
{
|
||||
return appNode->getInt(propName,defaultVal);
|
||||
}
|
||||
|
||||
/// Get a boolean property value
|
||||
///
|
||||
/// @param propName Name of the property to get
|
||||
/// @param defaultVal Reference to variable to hold return value
|
||||
///
|
||||
/// @return True if a value was set, false if not
|
||||
bool getBool(const char *propName, bool &defaultVal)
|
||||
{
|
||||
return appNode->getBool(propName,defaultVal);
|
||||
}
|
||||
|
||||
/// Return true if this mesh is a skin
|
||||
bool isSkin()
|
||||
{
|
||||
if (instanceCtrl) {
|
||||
const domController* ctrl = daeSafeCast<domController>(instanceCtrl->getUrl().getElement());
|
||||
if (ctrl && ctrl->getSkin() &&
|
||||
(ctrl->getSkin()->getVertex_weights()->getV()->getValue().getCount() > 0))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Get the skin data: bones, vertex weights etc
|
||||
void lookupSkinData();
|
||||
|
||||
/// Check if the mesh visibility is animated
|
||||
///
|
||||
/// @param appSeq Start/end time to check
|
||||
///
|
||||
/// @return True if the mesh visibility is animated, false if not
|
||||
bool animatesVis(const AppSequence* appSeq);
|
||||
|
||||
/// Check if the material used by this mesh is animated
|
||||
///
|
||||
/// @param appSeq Start/end time to check
|
||||
///
|
||||
/// @return True if the material is animated, false if not
|
||||
bool animatesMatFrame(const AppSequence* appSeq);
|
||||
|
||||
/// Check if the mesh is animated
|
||||
///
|
||||
/// @param appSeq Start/end time to check
|
||||
///
|
||||
/// @return True if the mesh is animated, false if not
|
||||
bool animatesFrame(const AppSequence* appSeq);
|
||||
|
||||
/// Generate the vertex, normal and triangle data for the mesh.
|
||||
///
|
||||
/// @param time Time at which to generate the mesh data
|
||||
/// @param objectOffset Transform to apply to the generated data (bounds transform)
|
||||
void lockMesh(F32 time, const MatrixF& objectOffset);
|
||||
|
||||
/// Get the transform of this mesh at a certain time
|
||||
///
|
||||
/// @param time Time at which to get the transform
|
||||
///
|
||||
/// @return The mesh transform at the specified time
|
||||
MatrixF getMeshTransform(F32 time);
|
||||
|
||||
/// Get the visibility of this mesh at a certain time
|
||||
///
|
||||
/// @param time Time at which to get visibility info
|
||||
///
|
||||
/// @return Visibility from 0 (invisible) to 1 (opaque)
|
||||
F32 getVisValue(F32 time);
|
||||
};
|
||||
|
||||
#endif // _COLLADA_APPMESH_H_
|
||||
260
Engine/source/ts/collada/colladaAppNode.cpp
Normal file
260
Engine/source/ts/collada/colladaAppNode.cpp
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4706) // disable warning about assignment within conditional
|
||||
#endif
|
||||
|
||||
#include "ts/loader/appSequence.h"
|
||||
#include "ts/collada/colladaExtensions.h"
|
||||
#include "ts/collada/colladaAppNode.h"
|
||||
#include "ts/collada/colladaAppMesh.h"
|
||||
#include "ts/collada/colladaAppMesh.h"
|
||||
|
||||
#include "core/stringTable.h"
|
||||
|
||||
// Trim leading and trailing whitespace from the first word in the string
|
||||
// Note that the string is modified.
|
||||
static char* TrimFirstWord(char* str)
|
||||
{
|
||||
char* value = str;
|
||||
|
||||
// Trim leading whitespace
|
||||
while ( value && *value && dIsspace( *value ) )
|
||||
value++;
|
||||
|
||||
// Trim trailing whitespace
|
||||
if ( value && *value )
|
||||
{
|
||||
char* end = value + 1;
|
||||
while ( *end && !dIsspace( *end ) )
|
||||
end++;
|
||||
*end = '\0';
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
ColladaAppNode::ColladaAppNode(const domNode* node, ColladaAppNode* parent)
|
||||
: p_domNode(node), appParent(parent), nodeExt(new ColladaExtension_node(node)),
|
||||
lastTransformTime(TSShapeLoader::DefaultTime-1), defaultTransformValid(false),
|
||||
invertMeshes(false)
|
||||
{
|
||||
mName = dStrdup(_GetNameOrId(node));
|
||||
mParentName = dStrdup(parent ? parent->getName() : "ROOT");
|
||||
|
||||
// Extract user properties from the <node> extension as whitespace separated
|
||||
// "name=value" pairs
|
||||
char* properties = dStrdup(nodeExt->user_properties);
|
||||
char* pos = properties;
|
||||
char* end = properties + dStrlen( properties );
|
||||
while ( pos < end )
|
||||
{
|
||||
// Find the '=' character to separate the name and value pair
|
||||
char* split = dStrchr( pos, '=' );
|
||||
if ( !split )
|
||||
break;
|
||||
|
||||
// Get the name (whitespace trimmed string up to the '=')
|
||||
// and value (whitespace trimmed string after the '=')
|
||||
*split = '\0';
|
||||
char* name = TrimFirstWord( pos );
|
||||
char* value = TrimFirstWord( split + 1 );
|
||||
|
||||
mProps.insert(StringTable->insert(name), dAtof(value));
|
||||
|
||||
pos = value + dStrlen( value ) + 1;
|
||||
}
|
||||
|
||||
dFree( properties );
|
||||
|
||||
// Create vector of transform elements
|
||||
for (int iChild = 0; iChild < node->getContents().getCount(); iChild++) {
|
||||
switch (node->getContents()[iChild]->getElementType()) {
|
||||
case COLLADA_TYPE::TRANSLATE:
|
||||
case COLLADA_TYPE::ROTATE:
|
||||
case COLLADA_TYPE::SCALE:
|
||||
case COLLADA_TYPE::SKEW:
|
||||
case COLLADA_TYPE::MATRIX:
|
||||
case COLLADA_TYPE::LOOKAT:
|
||||
nodeTransforms.increment();
|
||||
nodeTransforms.last().element = node->getContents()[iChild];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get all child nodes
|
||||
void ColladaAppNode::buildChildList()
|
||||
{
|
||||
// Process children: collect <node> and <instance_node> elements
|
||||
for (int iChild = 0; iChild < p_domNode->getContents().getCount(); iChild++) {
|
||||
|
||||
daeElement* child = p_domNode->getContents()[iChild];
|
||||
switch (child->getElementType()) {
|
||||
|
||||
case COLLADA_TYPE::NODE:
|
||||
{
|
||||
domNode* node = daeSafeCast<domNode>(child);
|
||||
mChildNodes.push_back(new ColladaAppNode(node, this));
|
||||
break;
|
||||
}
|
||||
|
||||
case COLLADA_TYPE::INSTANCE_NODE:
|
||||
{
|
||||
domInstance_node* instanceNode = daeSafeCast<domInstance_node>(child);
|
||||
domNode* node = daeSafeCast<domNode>(instanceNode->getUrl().getElement());
|
||||
if (node)
|
||||
mChildNodes.push_back(new ColladaAppNode(node, this));
|
||||
else
|
||||
Con::warnf("Failed to resolve instance_node with url=%s", instanceNode->getUrl().originalStr().c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get all geometry attached to this node
|
||||
void ColladaAppNode::buildMeshList()
|
||||
{
|
||||
// Process children: collect <instance_geometry> and <instance_controller> elements
|
||||
for (int iChild = 0; iChild < p_domNode->getContents().getCount(); iChild++) {
|
||||
|
||||
daeElement* child = p_domNode->getContents()[iChild];
|
||||
switch (child->getElementType()) {
|
||||
|
||||
case COLLADA_TYPE::INSTANCE_GEOMETRY:
|
||||
{
|
||||
// Only <geometry>.<mesh> instances are supported
|
||||
domInstance_geometry* instanceGeom = daeSafeCast<domInstance_geometry>(child);
|
||||
if (instanceGeom) {
|
||||
domGeometry* geometry = daeSafeCast<domGeometry>(instanceGeom->getUrl().getElement());
|
||||
if (geometry && geometry->getMesh())
|
||||
mMeshes.push_back(new ColladaAppMesh(instanceGeom, this));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case COLLADA_TYPE::INSTANCE_CONTROLLER:
|
||||
mMeshes.push_back(new ColladaAppMesh(daeSafeCast<domInstance_controller>(child), this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ColladaAppNode::animatesTransform(const AppSequence* appSeq)
|
||||
{
|
||||
// Check if any of this node's transform elements are animated during the
|
||||
// sequence interval
|
||||
for (int iTxfm = 0; iTxfm < nodeTransforms.size(); iTxfm++) {
|
||||
if (nodeTransforms[iTxfm].isAnimated(appSeq->getStart(), appSeq->getEnd()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Get the world transform of the node at the specified time
|
||||
MatrixF ColladaAppNode::getNodeTransform(F32 time)
|
||||
{
|
||||
// Avoid re-computing the default transform if possible
|
||||
if (defaultTransformValid && time == TSShapeLoader::DefaultTime)
|
||||
{
|
||||
return defaultNodeTransform;
|
||||
}
|
||||
else
|
||||
{
|
||||
MatrixF nodeTransform = getTransform(time);
|
||||
|
||||
// Check for inverted node coordinate spaces => can happen when modelers
|
||||
// use the 'mirror' tool in their 3d app. Shows up as negative <scale>
|
||||
// transforms in the collada model.
|
||||
if (m_matF_determinant(nodeTransform) < 0.0f)
|
||||
{
|
||||
// Mark this node as inverted so we can mirror mesh geometry, then
|
||||
// de-invert the transform matrix
|
||||
invertMeshes = true;
|
||||
nodeTransform.scale(Point3F(1, 1, -1));
|
||||
}
|
||||
|
||||
// Cache the default transform
|
||||
if (time == TSShapeLoader::DefaultTime)
|
||||
{
|
||||
defaultTransformValid = true;
|
||||
defaultNodeTransform = nodeTransform;
|
||||
}
|
||||
|
||||
return nodeTransform;
|
||||
}
|
||||
}
|
||||
|
||||
MatrixF ColladaAppNode::getTransform(F32 time)
|
||||
{
|
||||
// Check if we can use the last computed transform
|
||||
if (time == lastTransformTime)
|
||||
return lastTransform;
|
||||
|
||||
if (appParent) {
|
||||
// Get parent node's transform
|
||||
lastTransform = appParent->getTransform(time);
|
||||
}
|
||||
else {
|
||||
// no parent (ie. root level) => scale by global shape <unit>
|
||||
lastTransform.identity();
|
||||
lastTransform.scale(ColladaUtils::getOptions().unit);
|
||||
if (!isBounds())
|
||||
ColladaUtils::convertTransform(lastTransform); // don't convert bounds node transform (or upAxis won't work!)
|
||||
}
|
||||
|
||||
// Multiply by local node transform elements
|
||||
for (int iTxfm = 0; iTxfm < nodeTransforms.size(); iTxfm++) {
|
||||
|
||||
MatrixF mat(true);
|
||||
|
||||
// Convert the transform element to a MatrixF
|
||||
switch (nodeTransforms[iTxfm].element->getElementType()) {
|
||||
case COLLADA_TYPE::TRANSLATE: mat = vecToMatrixF<domTranslate>(nodeTransforms[iTxfm].getValue(time)); break;
|
||||
case COLLADA_TYPE::SCALE: mat = vecToMatrixF<domScale>(nodeTransforms[iTxfm].getValue(time)); break;
|
||||
case COLLADA_TYPE::ROTATE: mat = vecToMatrixF<domRotate>(nodeTransforms[iTxfm].getValue(time)); break;
|
||||
case COLLADA_TYPE::MATRIX: mat = vecToMatrixF<domMatrix>(nodeTransforms[iTxfm].getValue(time)); break;
|
||||
case COLLADA_TYPE::SKEW: mat = vecToMatrixF<domSkew>(nodeTransforms[iTxfm].getValue(time)); break;
|
||||
case COLLADA_TYPE::LOOKAT: mat = vecToMatrixF<domLookat>(nodeTransforms[iTxfm].getValue(time)); break;
|
||||
}
|
||||
|
||||
// Remove node scaling (but keep reflections) if desired
|
||||
if (ColladaUtils::getOptions().ignoreNodeScale)
|
||||
{
|
||||
Point3F invScale = mat.getScale();
|
||||
invScale.x = invScale.x ? (1.0f / invScale.x) : 0;
|
||||
invScale.y = invScale.y ? (1.0f / invScale.y) : 0;
|
||||
invScale.z = invScale.z ? (1.0f / invScale.z) : 0;
|
||||
mat.scale(invScale);
|
||||
}
|
||||
|
||||
// Post multiply the animated transform
|
||||
lastTransform.mul(mat);
|
||||
}
|
||||
|
||||
lastTransformTime = time;
|
||||
return lastTransform;
|
||||
}
|
||||
110
Engine/source/ts/collada/colladaAppNode.h
Normal file
110
Engine/source/ts/collada/colladaAppNode.h
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _COLLADA_APPNODE_H_
|
||||
#define _COLLADA_APPNODE_H_
|
||||
|
||||
#ifndef _TDICTIONARY_H_
|
||||
#include "core/tDictionary.h"
|
||||
#endif
|
||||
#ifndef _APPNODE_H_
|
||||
#include "ts/loader/appNode.h"
|
||||
#endif
|
||||
#ifndef _COLLADA_EXTENSIONS_H_
|
||||
#include "ts/collada/colladaExtensions.h"
|
||||
#endif
|
||||
|
||||
class ColladaAppNode : public AppNode
|
||||
{
|
||||
typedef AppNode Parent;
|
||||
friend class ColladaAppMesh;
|
||||
|
||||
MatrixF getTransform(F32 time);
|
||||
void buildMeshList();
|
||||
void buildChildList();
|
||||
|
||||
protected:
|
||||
|
||||
const domNode* p_domNode; ///< Pointer to the node in the Collada DOM
|
||||
ColladaAppNode* appParent; ///< Parent node in Collada-space
|
||||
ColladaExtension_node* nodeExt; ///< node extension
|
||||
Vector<AnimatedFloatList> nodeTransforms; ///< Ordered vector of node transform elements (scale, translate etc)
|
||||
bool invertMeshes; ///< True if this node's coordinate space is inverted (left handed)
|
||||
|
||||
Map<StringTableEntry, F32> mProps; ///< Hash of float properties (converted to int or bool as needed)
|
||||
|
||||
F32 lastTransformTime; ///< Time of the last transform lookup (getTransform)
|
||||
MatrixF lastTransform; ///< Last transform lookup (getTransform)
|
||||
bool defaultTransformValid; ///< Flag indicating whether the defaultNodeTransform is valid
|
||||
MatrixF defaultNodeTransform; ///< Transform at DefaultTime
|
||||
|
||||
public:
|
||||
|
||||
ColladaAppNode(const domNode* node, ColladaAppNode* parent = 0);
|
||||
virtual ~ColladaAppNode()
|
||||
{
|
||||
delete nodeExt;
|
||||
mProps.clear();
|
||||
}
|
||||
|
||||
const domNode* getDomNode() const { return p_domNode; }
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
const char *getName() { return mName; }
|
||||
const char *getParentName() { return mParentName; }
|
||||
|
||||
bool isEqual(AppNode* node)
|
||||
{
|
||||
const ColladaAppNode* appNode = dynamic_cast<const ColladaAppNode*>(node);
|
||||
return (appNode && (appNode->p_domNode == p_domNode));
|
||||
}
|
||||
|
||||
// Property look-ups: only float properties are stored, the rest are
|
||||
// converted from floats as needed
|
||||
bool getFloat(const char* propName, F32& defaultVal)
|
||||
{
|
||||
Map<StringTableEntry,F32>::Iterator itr = mProps.find(propName);
|
||||
if (itr != mProps.end())
|
||||
defaultVal = itr->value;
|
||||
return false;
|
||||
}
|
||||
bool getInt(const char* propName, S32& defaultVal)
|
||||
{
|
||||
F32 value = defaultVal;
|
||||
bool ret = getFloat(propName, value);
|
||||
defaultVal = (S32)value;
|
||||
return ret;
|
||||
}
|
||||
bool getBool(const char* propName, bool& defaultVal)
|
||||
{
|
||||
F32 value = defaultVal;
|
||||
bool ret = getFloat(propName, value);
|
||||
defaultVal = (value != 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
MatrixF getNodeTransform(F32 time);
|
||||
bool animatesTransform(const AppSequence* appSeq);
|
||||
bool isParentRoot() { return (appParent == NULL); }
|
||||
};
|
||||
|
||||
#endif // _COLLADA_APPNODE_H_
|
||||
97
Engine/source/ts/collada/colladaAppSequence.cpp
Normal file
97
Engine/source/ts/collada/colladaAppSequence.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/collada/colladaExtensions.h"
|
||||
#include "ts/collada/colladaAppSequence.h"
|
||||
|
||||
|
||||
ColladaAppSequence::ColladaAppSequence(const domAnimation_clip* clip)
|
||||
: pClip(clip), clipExt(new ColladaExtension_animation_clip(clip))
|
||||
{
|
||||
seqStart = pClip->getStart();
|
||||
seqEnd = pClip->getEnd();
|
||||
}
|
||||
|
||||
ColladaAppSequence::~ColladaAppSequence()
|
||||
{
|
||||
delete clipExt;
|
||||
}
|
||||
|
||||
const char* ColladaAppSequence::getName() const
|
||||
{
|
||||
return _GetNameOrId(pClip);
|
||||
}
|
||||
|
||||
S32 ColladaAppSequence::getNumTriggers()
|
||||
{
|
||||
return clipExt->triggers.size();
|
||||
}
|
||||
|
||||
void ColladaAppSequence::getTrigger(S32 index, TSShape::Trigger& trigger)
|
||||
{
|
||||
trigger.pos = clipExt->triggers[index].time;
|
||||
trigger.state = clipExt->triggers[index].state;
|
||||
}
|
||||
|
||||
U32 ColladaAppSequence::getFlags() const
|
||||
{
|
||||
U32 flags = 0;
|
||||
if (clipExt->cyclic) flags |= TSShape::Cyclic;
|
||||
if (clipExt->blend) flags |= TSShape::Blend;
|
||||
return flags;
|
||||
}
|
||||
|
||||
F32 ColladaAppSequence::getPriority()
|
||||
{
|
||||
return clipExt->priority;
|
||||
}
|
||||
|
||||
F32 ColladaAppSequence::getBlendRefTime()
|
||||
{
|
||||
return clipExt->blendReferenceTime;
|
||||
}
|
||||
|
||||
void ColladaAppSequence::setActive(bool active)
|
||||
{
|
||||
for (int iAnim = 0; iAnim < getClip()->getInstance_animation_array().getCount(); iAnim++) {
|
||||
domAnimation* anim = daeSafeCast<domAnimation>(getClip()->getInstance_animation_array()[iAnim]->getUrl().getElement());
|
||||
if (anim)
|
||||
setAnimationActive(anim, active);
|
||||
}
|
||||
}
|
||||
|
||||
void ColladaAppSequence::setAnimationActive(const domAnimation* anim, bool active)
|
||||
{
|
||||
// Enabled/disable data channels for this animation
|
||||
for (int iChannel = 0; iChannel < anim->getChannel_array().getCount(); iChannel++) {
|
||||
domChannel* channel = anim->getChannel_array()[iChannel];
|
||||
AnimData* animData = reinterpret_cast<AnimData*>(channel->getUserData());
|
||||
if (animData)
|
||||
animData->enabled = active;
|
||||
}
|
||||
|
||||
// Recurse into child animations
|
||||
for (int iAnim = 0; iAnim < anim->getAnimation_array().getCount(); iAnim++)
|
||||
setAnimationActive(anim->getAnimation_array()[iAnim], active);
|
||||
}
|
||||
65
Engine/source/ts/collada/colladaAppSequence.h
Normal file
65
Engine/source/ts/collada/colladaAppSequence.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _COLLADA_APPSEQUENCE_H_
|
||||
#define _COLLADA_APPSEQUENCE_H_
|
||||
|
||||
#ifndef _APPSEQUENCE_H_
|
||||
#include "ts/loader/appSequence.h"
|
||||
#endif
|
||||
|
||||
class domAnimation_clip;
|
||||
class ColladaExtension_animation_clip;
|
||||
|
||||
class ColladaAppSequence : public AppSequence
|
||||
{
|
||||
const domAnimation_clip* pClip;
|
||||
ColladaExtension_animation_clip* clipExt;
|
||||
|
||||
F32 seqStart;
|
||||
F32 seqEnd;
|
||||
|
||||
void setAnimationActive(const domAnimation* anim, bool active);
|
||||
|
||||
public:
|
||||
ColladaAppSequence(const domAnimation_clip* clip);
|
||||
~ColladaAppSequence();
|
||||
|
||||
void setActive(bool active);
|
||||
|
||||
const domAnimation_clip* getClip() const { return pClip; }
|
||||
|
||||
S32 getNumTriggers();
|
||||
void getTrigger(S32 index, TSShape::Trigger& trigger);
|
||||
|
||||
const char* getName() const;
|
||||
|
||||
F32 getStart() const { return seqStart; }
|
||||
F32 getEnd() const { return seqEnd; }
|
||||
void setEnd(F32 end) { seqEnd = end; }
|
||||
|
||||
U32 getFlags() const;
|
||||
F32 getPriority();
|
||||
F32 getBlendRefTime();
|
||||
};
|
||||
|
||||
#endif // _COLLADA_APPSEQUENCE_H_
|
||||
62
Engine/source/ts/collada/colladaExtensions.cpp
Normal file
62
Engine/source/ts/collada/colladaExtensions.cpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "math/mRandom.h"
|
||||
#include "ts/collada/colladaExtensions.h"
|
||||
|
||||
/// Check if any of the MAYA texture transform elements are animated within
|
||||
/// the interval
|
||||
bool ColladaExtension_effect::animatesTextureTransform(F32 start, F32 end)
|
||||
{
|
||||
return repeatU.isAnimated(start, end) || repeatV.isAnimated(start, end) ||
|
||||
offsetU.isAnimated(start, end) || offsetV.isAnimated(start, end) ||
|
||||
rotateUV.isAnimated(start, end) || noiseU.isAnimated(start, end) ||
|
||||
noiseV.isAnimated(start, end);
|
||||
}
|
||||
|
||||
/// Apply the MAYA texture transform to the given UV coordinates
|
||||
void ColladaExtension_effect::applyTextureTransform(Point2F& uv, F32 time)
|
||||
{
|
||||
// This function will be called for every tvert, every frame. So cache the
|
||||
// texture transform parameters to avoid interpolating them every call (since
|
||||
// they are constant for all tverts for a given 't')
|
||||
if (time != lastAnimTime) {
|
||||
// Update texture transform
|
||||
textureTransform.set(EulerF(0, 0, rotateUV.getValue(time)));
|
||||
textureTransform.setPosition(Point3F(
|
||||
offsetU.getValue(time) + noiseU.getValue(time)*gRandGen.randF(),
|
||||
offsetV.getValue(time) + noiseV.getValue(time)*gRandGen.randF(),
|
||||
0));
|
||||
textureTransform.scale(Point3F(repeatU.getValue(time), repeatV.getValue(time), 1.0f));
|
||||
|
||||
lastAnimTime = time;
|
||||
}
|
||||
|
||||
// Apply texture transform
|
||||
Point3F result;
|
||||
textureTransform.mulP(Point3F(uv.x, uv.y, 0), &result);
|
||||
|
||||
uv.x = result.x;
|
||||
uv.y = result.y;
|
||||
}
|
||||
305
Engine/source/ts/collada/colladaExtensions.h
Normal file
305
Engine/source/ts/collada/colladaExtensions.h
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _COLLADA_EXTENSIONS_H_
|
||||
#define _COLLADA_EXTENSIONS_H_
|
||||
|
||||
#ifndef _TSSHAPE_LOADER_H_
|
||||
#include "ts/loader/tsShapeLoader.h"
|
||||
#endif
|
||||
#ifndef _COLLADA_UTILS_H_
|
||||
#include "ts/collada/colladaUtils.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Collada allows custom data to be included with many elements using the <extra>
|
||||
// tag, followed by one or more named technique profiles. eg.
|
||||
// <some_element>
|
||||
// <extra>
|
||||
// <technique profile="SOME_PROFILE">
|
||||
// <custom_element0>value0</custom_element0>
|
||||
// <custom_element1>value1</custom_element1>
|
||||
// ...
|
||||
// <technique profile="ANOTHER_PROFILE">
|
||||
// <custom_element0>value0</custom_element0>
|
||||
// <custom_element1>value1</custom_element1>
|
||||
// ...
|
||||
//
|
||||
// This class provides an easy way to read the custom parameters into a strongly
|
||||
// typed subclass.
|
||||
class ColladaExtension
|
||||
{
|
||||
// Helper macro to simplify getting named parameters
|
||||
#define GET_EXTRA_PARAM(param, defaultVal) \
|
||||
get(#param, param, defaultVal)
|
||||
|
||||
protected:
|
||||
const domTechnique* pTechnique;
|
||||
|
||||
/// Find the technique with the named profile
|
||||
template<class T> const domTechnique* findExtraTechnique(const T* element, const char* name) const
|
||||
{
|
||||
if (element) {
|
||||
for (int iExt = 0; iExt < element->getExtra_array().getCount(); iExt++) {
|
||||
for (int iTech = 0; iTech < element->getExtra_array()[iExt]->getTechnique_array().getCount(); iTech++) {
|
||||
if (dStrEqual(element->getExtra_array()[iExt]->getTechnique_array()[iTech]->getProfile(), name))
|
||||
return element->getExtra_array()[iExt]->getTechnique_array()[iTech];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// The <texture> element does not define an extra_array, so need a specialized
|
||||
/// version of the template
|
||||
const domTechnique* findExtraTechnique(
|
||||
const domCommon_color_or_texture_type_complexType::domTexture* element, const char* name) const
|
||||
{
|
||||
if (element && element->getExtra()) {
|
||||
for (int iTech = 0; iTech < element->getExtra()->getTechnique_array().getCount(); iTech++) {
|
||||
if (dStrEqual(element->getExtra()->getTechnique_array()[iTech]->getProfile(), name))
|
||||
return element->getExtra()->getTechnique_array()[iTech];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Find the parameter with the given name
|
||||
const domAny* findParam(const char* name)
|
||||
{
|
||||
if (pTechnique) {
|
||||
// search the technique contents for the desired parameter
|
||||
for (int iParam = 0; iParam < pTechnique->getContents().getCount(); iParam++) {
|
||||
const domAny* param = daeSafeCast<domAny>(pTechnique->getContents()[iParam]);
|
||||
if (param && !dStrcmp(param->getElementName(), name))
|
||||
return param;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Get the value of the named parameter (use defaultVal if parameter not found)
|
||||
template<typename T> void get(const char* name, T& value, T defaultVal)
|
||||
{
|
||||
value = defaultVal;
|
||||
if (const domAny* param = findParam(name))
|
||||
value = convert<T>(param->getValue());
|
||||
}
|
||||
|
||||
/// Get the value of the named animated parameter (use defaultVal if parameter not found)
|
||||
template<typename T> void get(const char* name, AnimatedElement<T>& value, T defaultVal)
|
||||
{
|
||||
value.defaultVal = defaultVal;
|
||||
if (const domAny* param = findParam(name))
|
||||
value.element = param;
|
||||
}
|
||||
|
||||
public:
|
||||
ColladaExtension() : pTechnique(0) { }
|
||||
virtual ~ColladaExtension() { }
|
||||
};
|
||||
|
||||
/// Extensions for the <effect> element (and its children)
|
||||
class ColladaExtension_effect : public ColladaExtension
|
||||
{
|
||||
// Cached texture transform
|
||||
F32 lastAnimTime;
|
||||
MatrixF textureTransform;
|
||||
|
||||
public:
|
||||
//----------------------------------
|
||||
// <effect>
|
||||
// MAX3D profile elements
|
||||
bool double_sided;
|
||||
|
||||
//----------------------------------
|
||||
// <effect>.<profile_COMMON>
|
||||
// GOOGLEEARTH profile elements
|
||||
//bool double_sided;
|
||||
|
||||
//----------------------------------
|
||||
// <effect>.<profile_COMMON>.<technique>.<blinn/phong/lambert>.<diffuse>.<texture>
|
||||
// MAYA profile elements
|
||||
bool wrapU, wrapV;
|
||||
bool mirrorU, mirrorV;
|
||||
AnimatedFloat coverageU, coverageV;
|
||||
AnimatedFloat translateFrameU, translateFrameV;
|
||||
AnimatedFloat rotateFrame;
|
||||
AnimatedBool stagger; // @todo: not supported yet
|
||||
AnimatedFloat repeatU, repeatV;
|
||||
AnimatedFloat offsetU, offsetV;
|
||||
AnimatedFloat rotateUV;
|
||||
AnimatedFloat noiseU, noiseV;
|
||||
|
||||
//----------------------------------
|
||||
// <effect>.<profile_COMMON>.<technique>
|
||||
// FCOLLADA profile elements
|
||||
domFx_sampler2D_common_complexType* bumpSampler;
|
||||
|
||||
public:
|
||||
ColladaExtension_effect(const domEffect* effect)
|
||||
: lastAnimTime(TSShapeLoader::DefaultTime-1), textureTransform(true), bumpSampler(0)
|
||||
{
|
||||
//----------------------------------
|
||||
// <effect>
|
||||
// MAX3D profile
|
||||
pTechnique = findExtraTechnique(effect, "MAX3D");
|
||||
GET_EXTRA_PARAM(double_sided, false);
|
||||
|
||||
//----------------------------------
|
||||
// <effect>.<profile_COMMON>
|
||||
const domProfile_COMMON* profileCommon = ColladaUtils::findEffectCommonProfile(effect);
|
||||
|
||||
// GOOGLEEARTH profile (same double_sided element)
|
||||
pTechnique = findExtraTechnique(profileCommon, "GOOGLEEARTH");
|
||||
GET_EXTRA_PARAM(double_sided, double_sided);
|
||||
|
||||
//----------------------------------
|
||||
// <effect>.<profile_COMMON>.<technique>.<blinn/phong/lambert>.<diffuse>.<texture>
|
||||
const domCommon_color_or_texture_type_complexType* domDiffuse = ColladaUtils::findEffectDiffuse(effect);
|
||||
const domFx_sampler2D_common_complexType* sampler2D = ColladaUtils::getTextureSampler(effect, domDiffuse);
|
||||
|
||||
// Use the sampler2D to set default values for wrap/mirror flags
|
||||
wrapU = wrapV = true;
|
||||
mirrorU = mirrorV = false;
|
||||
if (sampler2D) {
|
||||
domFx_sampler2D_common_complexType::domWrap_s* wrap_s = sampler2D->getWrap_s();
|
||||
domFx_sampler2D_common_complexType::domWrap_t* wrap_t = sampler2D->getWrap_t();
|
||||
|
||||
mirrorU = (wrap_s && wrap_s->getValue() == FX_SAMPLER_WRAP_COMMON_MIRROR);
|
||||
wrapU = (mirrorU || !wrap_s || (wrap_s->getValue() == FX_SAMPLER_WRAP_COMMON_WRAP));
|
||||
mirrorV = (wrap_t && wrap_t->getValue() == FX_SAMPLER_WRAP_COMMON_MIRROR);
|
||||
wrapV = (mirrorV || !wrap_t || (wrap_t->getValue() == FX_SAMPLER_WRAP_COMMON_WRAP));
|
||||
}
|
||||
|
||||
// MAYA profile
|
||||
pTechnique = findExtraTechnique(domDiffuse ? domDiffuse->getTexture() : 0, "MAYA");
|
||||
GET_EXTRA_PARAM(wrapU, wrapU); GET_EXTRA_PARAM(wrapV, wrapV);
|
||||
GET_EXTRA_PARAM(mirrorU, mirrorU); GET_EXTRA_PARAM(mirrorV, mirrorV);
|
||||
GET_EXTRA_PARAM(coverageU, 1.0); GET_EXTRA_PARAM(coverageV, 1.0);
|
||||
GET_EXTRA_PARAM(translateFrameU, 0.0); GET_EXTRA_PARAM(translateFrameV, 0.0);
|
||||
GET_EXTRA_PARAM(rotateFrame, 0.0);
|
||||
GET_EXTRA_PARAM(stagger, false);
|
||||
GET_EXTRA_PARAM(repeatU, 1.0); GET_EXTRA_PARAM(repeatV, 1.0);
|
||||
GET_EXTRA_PARAM(offsetU, 0.0); GET_EXTRA_PARAM(offsetV, 0.0);
|
||||
GET_EXTRA_PARAM(rotateUV, 0.0);
|
||||
GET_EXTRA_PARAM(noiseU, 0.0); GET_EXTRA_PARAM(noiseV, 0.0);
|
||||
|
||||
// FCOLLADA profile
|
||||
if (profileCommon) {
|
||||
pTechnique = findExtraTechnique((const domProfile_COMMON::domTechnique*)profileCommon->getTechnique(), "FCOLLADA");
|
||||
if (pTechnique) {
|
||||
domAny* bump = daeSafeCast<domAny>(const_cast<domTechnique*>(pTechnique)->getChild("bump"));
|
||||
if (bump) {
|
||||
domAny* bumpTexture = daeSafeCast<domAny>(bump->getChild("texture"));
|
||||
if (bumpTexture) {
|
||||
daeSIDResolver resolver(const_cast<domEffect*>(effect), bumpTexture->getAttribute("texture").c_str());
|
||||
domCommon_newparam_type* param = daeSafeCast<domCommon_newparam_type>(resolver.getElement());
|
||||
if (param)
|
||||
bumpSampler = param->getSampler2D();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if any of the MAYA texture transform elements are animated within
|
||||
/// the interval
|
||||
bool animatesTextureTransform(F32 start, F32 end);
|
||||
|
||||
/// Apply the MAYA texture transform to the given UV coordinates
|
||||
void applyTextureTransform(Point2F& uv, F32 time);
|
||||
};
|
||||
|
||||
/// Extensions for the <node> element
|
||||
class ColladaExtension_node : public ColladaExtension
|
||||
{
|
||||
public:
|
||||
// FCOLLADA or OpenCOLLADA profile elements
|
||||
AnimatedFloat visibility;
|
||||
const char* user_properties;
|
||||
|
||||
ColladaExtension_node(const domNode* node)
|
||||
{
|
||||
// FCOLLADA profile
|
||||
pTechnique = findExtraTechnique(node, "FCOLLADA");
|
||||
GET_EXTRA_PARAM(visibility, 1.0);
|
||||
GET_EXTRA_PARAM(user_properties, "");
|
||||
|
||||
// OpenCOLLADA profile
|
||||
pTechnique = findExtraTechnique(node, "OpenCOLLADA");
|
||||
if (!visibility.element)
|
||||
GET_EXTRA_PARAM(visibility, 1.0);
|
||||
GET_EXTRA_PARAM(user_properties, user_properties);
|
||||
}
|
||||
};
|
||||
|
||||
/// Extensions for the <geometry> element
|
||||
class ColladaExtension_geometry : public ColladaExtension
|
||||
{
|
||||
public:
|
||||
// MAYA profile elements
|
||||
bool double_sided;
|
||||
|
||||
ColladaExtension_geometry(const domGeometry* geometry)
|
||||
{
|
||||
// MAYA profile
|
||||
pTechnique = findExtraTechnique(geometry, "MAYA");
|
||||
GET_EXTRA_PARAM(double_sided, false);
|
||||
}
|
||||
};
|
||||
|
||||
// Extensions for the <animation_clip> element
|
||||
class ColladaExtension_animation_clip : public ColladaExtension
|
||||
{
|
||||
public:
|
||||
struct Trigger {
|
||||
F32 time;
|
||||
S32 state;
|
||||
};
|
||||
|
||||
// Torque profile elements (none of these are animatable)
|
||||
S32 num_triggers;
|
||||
Vector<Trigger> triggers;
|
||||
bool cyclic;
|
||||
bool blend;
|
||||
F32 blendReferenceTime;
|
||||
F32 priority;
|
||||
|
||||
ColladaExtension_animation_clip(const domAnimation_clip* clip)
|
||||
{
|
||||
// Torque profile
|
||||
pTechnique = findExtraTechnique(clip, "Torque");
|
||||
GET_EXTRA_PARAM(num_triggers, 0);
|
||||
for (int iTrigger = 0; iTrigger < num_triggers; iTrigger++) {
|
||||
triggers.increment();
|
||||
get(avar("trigger_time%d", iTrigger), triggers.last().time, 0.0f);
|
||||
get(avar("trigger_state%d", iTrigger), triggers.last().state, 0);
|
||||
}
|
||||
GET_EXTRA_PARAM(cyclic, false);
|
||||
GET_EXTRA_PARAM(blend, false);
|
||||
GET_EXTRA_PARAM(blendReferenceTime, 0.0f);
|
||||
GET_EXTRA_PARAM(priority, 5.0f);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _COLLADA_EXTENSIONS_H_
|
||||
259
Engine/source/ts/collada/colladaImport.cpp
Normal file
259
Engine/source/ts/collada/colladaImport.cpp
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "core/volume.h"
|
||||
#include "ts/collada/colladaUtils.h"
|
||||
#include "ts/collada/colladaAppNode.h"
|
||||
#include "ts/collada/colladaShapeLoader.h"
|
||||
|
||||
#include "gui/controls/guiTreeViewCtrl.h"
|
||||
|
||||
// Helper struct for counting nodes, meshes and polygons down through the scene
|
||||
// hierarchy
|
||||
struct SceneStats
|
||||
{
|
||||
S32 numNodes;
|
||||
S32 numMeshes;
|
||||
S32 numPolygons;
|
||||
S32 numMaterials;
|
||||
S32 numLights;
|
||||
S32 numClips;
|
||||
|
||||
SceneStats() : numNodes(0), numMeshes(0), numPolygons(0), numMaterials(0), numLights(0), numClips(0) { }
|
||||
};
|
||||
|
||||
// Recurse through the <visual_scene> adding nodes and geometry to the GuiTreeView control
|
||||
static void processNode(GuiTreeViewCtrl* tree, domNode* node, S32 parentID, SceneStats& stats)
|
||||
{
|
||||
stats.numNodes++;
|
||||
S32 nodeID = tree->insertItem(parentID, _GetNameOrId(node), "node", "", 0, 0);
|
||||
|
||||
// Update mesh and poly counts
|
||||
for (int i = 0; i < node->getContents().getCount(); i++)
|
||||
{
|
||||
domGeometry* geom = 0;
|
||||
const char* elemName = "";
|
||||
|
||||
daeElement* child = node->getContents()[i];
|
||||
switch (child->getElementType())
|
||||
{
|
||||
case COLLADA_TYPE::INSTANCE_GEOMETRY:
|
||||
{
|
||||
domInstance_geometry* instgeom = daeSafeCast<domInstance_geometry>(child);
|
||||
if (instgeom)
|
||||
{
|
||||
geom = daeSafeCast<domGeometry>(instgeom->getUrl().getElement());
|
||||
elemName = _GetNameOrId(geom);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case COLLADA_TYPE::INSTANCE_CONTROLLER:
|
||||
{
|
||||
domInstance_controller* instctrl = daeSafeCast<domInstance_controller>(child);
|
||||
if (instctrl)
|
||||
{
|
||||
domController* ctrl = daeSafeCast<domController>(instctrl->getUrl().getElement());
|
||||
elemName = _GetNameOrId(ctrl);
|
||||
if (ctrl && ctrl->getSkin())
|
||||
geom = daeSafeCast<domGeometry>(ctrl->getSkin()->getSource().getElement());
|
||||
else if (ctrl && ctrl->getMorph())
|
||||
geom = daeSafeCast<domGeometry>(ctrl->getMorph()->getSource().getElement());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case COLLADA_TYPE::INSTANCE_LIGHT:
|
||||
stats.numLights++;
|
||||
tree->insertItem(nodeID, _GetNameOrId(node), "light", "", 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (geom && geom->getMesh())
|
||||
{
|
||||
const char* name = _GetNameOrId(node);
|
||||
if ( dStrEqual( name, "null" ) || dStrEndsWith( name, "PIVOT" ) )
|
||||
name = _GetNameOrId( daeSafeCast<domNode>(node->getParent()) );
|
||||
|
||||
stats.numMeshes++;
|
||||
tree->insertItem(nodeID, name, "mesh", "", 0, 0);
|
||||
|
||||
for (S32 j = 0; j < geom->getMesh()->getTriangles_array().getCount(); j++)
|
||||
stats.numPolygons += geom->getMesh()->getTriangles_array()[j]->getCount();
|
||||
for (S32 j = 0; j < geom->getMesh()->getTristrips_array().getCount(); j++)
|
||||
stats.numPolygons += geom->getMesh()->getTristrips_array()[j]->getCount();
|
||||
for (S32 j = 0; j < geom->getMesh()->getTrifans_array().getCount(); j++)
|
||||
stats.numPolygons += geom->getMesh()->getTrifans_array()[j]->getCount();
|
||||
for (S32 j = 0; j < geom->getMesh()->getPolygons_array().getCount(); j++)
|
||||
stats.numPolygons += geom->getMesh()->getPolygons_array()[j]->getCount();
|
||||
for (S32 j = 0; j < geom->getMesh()->getPolylist_array().getCount(); j++)
|
||||
stats.numPolygons += geom->getMesh()->getPolylist_array()[j]->getCount();
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse into child nodes
|
||||
for (S32 i = 0; i < node->getNode_array().getCount(); i++)
|
||||
processNode(tree, node->getNode_array()[i], nodeID, stats);
|
||||
|
||||
for (S32 i = 0; i < node->getInstance_node_array().getCount(); i++)
|
||||
{
|
||||
domInstance_node* instnode = node->getInstance_node_array()[i];
|
||||
domNode* node = daeSafeCast<domNode>(instnode->getUrl().getElement());
|
||||
if (node)
|
||||
processNode(tree, node, nodeID, stats);
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleFunction( enumColladaForImport, bool, 3, 3,
|
||||
"(string shapePath, GuiTreeViewCtrl ctrl) Collect scene information from "
|
||||
"a COLLADA file and store it in a GuiTreeView control. This function is "
|
||||
"used by the COLLADA import gui to show a preview of the scene contents "
|
||||
"prior to import, and is probably not much use for anything else.\n"
|
||||
"@param shapePath COLLADA filename\n"
|
||||
"@param ctrl GuiTreeView control to add elements to\n"
|
||||
"@return true if successful, false otherwise\n"
|
||||
"@ingroup Editors\n"
|
||||
"@internal")
|
||||
{
|
||||
GuiTreeViewCtrl* tree;
|
||||
if (!Sim::findObject(argv[2], tree))
|
||||
{
|
||||
Con::errorf("enumColladaScene::Could not find GuiTreeViewCtrl '%s'", argv[2]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if a cached DTS is available => no need to import the collada file
|
||||
// if we can load the DTS instead
|
||||
Torque::Path path(argv[1]);
|
||||
if (ColladaShapeLoader::canLoadCachedDTS(path))
|
||||
return false;
|
||||
|
||||
// Check if this is a Sketchup file (.kmz) and if so, mount the zip filesystem
|
||||
// and get the path to the DAE file.
|
||||
String mountPoint;
|
||||
Torque::Path daePath;
|
||||
bool isSketchup = ColladaShapeLoader::checkAndMountSketchup(path, mountPoint, daePath);
|
||||
|
||||
// Load the Collada file into memory
|
||||
domCOLLADA* root = ColladaShapeLoader::getDomCOLLADA(daePath);
|
||||
if (!root)
|
||||
{
|
||||
TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Load complete");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isSketchup)
|
||||
{
|
||||
// Unmount the zip if we mounted it
|
||||
Torque::FS::Unmount(mountPoint);
|
||||
}
|
||||
|
||||
// Initialize tree
|
||||
tree->removeItem(0);
|
||||
S32 nodesID = tree->insertItem(0, "Shape", "", "", 0, 0);
|
||||
S32 matsID = tree->insertItem(0, "Materials", "", "", 0, 0);
|
||||
S32 animsID = tree->insertItem(0, "Animations", "", "", 0, 0);
|
||||
|
||||
SceneStats stats;
|
||||
|
||||
// Query DOM for shape summary details
|
||||
for (int i = 0; i < root->getLibrary_visual_scenes_array().getCount(); i++)
|
||||
{
|
||||
const domLibrary_visual_scenes* libScenes = root->getLibrary_visual_scenes_array()[i];
|
||||
for (int j = 0; j < libScenes->getVisual_scene_array().getCount(); j++)
|
||||
{
|
||||
const domVisual_scene* visualScene = libScenes->getVisual_scene_array()[j];
|
||||
for (int k = 0; k < visualScene->getNode_array().getCount(); k++)
|
||||
processNode(tree, visualScene->getNode_array()[k], nodesID, stats);
|
||||
}
|
||||
}
|
||||
|
||||
// Get material count
|
||||
for (S32 i = 0; i < root->getLibrary_materials_array().getCount(); i++)
|
||||
{
|
||||
const domLibrary_materials* libraryMats = root->getLibrary_materials_array()[i];
|
||||
stats.numMaterials += libraryMats->getMaterial_array().getCount();
|
||||
for (S32 j = 0; j < libraryMats->getMaterial_array().getCount(); j++)
|
||||
{
|
||||
domMaterial* mat = libraryMats->getMaterial_array()[j];
|
||||
tree->insertItem(matsID, _GetNameOrId(mat), _GetNameOrId(mat), "", 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Get animation count
|
||||
for (S32 i = 0; i < root->getLibrary_animation_clips_array().getCount(); i++)
|
||||
{
|
||||
const domLibrary_animation_clips* libraryClips = root->getLibrary_animation_clips_array()[i];
|
||||
stats.numClips += libraryClips->getAnimation_clip_array().getCount();
|
||||
for (S32 j = 0; j < libraryClips->getAnimation_clip_array().getCount(); j++)
|
||||
{
|
||||
domAnimation_clip* clip = libraryClips->getAnimation_clip_array()[j];
|
||||
tree->insertItem(animsID, _GetNameOrId(clip), "animation", "", 0, 0);
|
||||
}
|
||||
}
|
||||
if (stats.numClips == 0)
|
||||
{
|
||||
// No clips => check if there are any animations (these will be added to a default clip)
|
||||
for (S32 i = 0; i < root->getLibrary_animations_array().getCount(); i++)
|
||||
{
|
||||
const domLibrary_animations* libraryAnims = root->getLibrary_animations_array()[i];
|
||||
if (libraryAnims->getAnimation_array().getCount())
|
||||
{
|
||||
stats.numClips = 1;
|
||||
tree->insertItem(animsID, "ambient", "animation", "", 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the global scale and up_axis from the top level <asset> element,
|
||||
F32 unit = 1.0f;
|
||||
domUpAxisType upAxis = UPAXISTYPE_Z_UP;
|
||||
if (root->getAsset()) {
|
||||
if (root->getAsset()->getUnit())
|
||||
unit = root->getAsset()->getUnit()->getMeter();
|
||||
if (root->getAsset()->getUp_axis())
|
||||
upAxis = root->getAsset()->getUp_axis()->getValue();
|
||||
}
|
||||
|
||||
TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Load complete");
|
||||
|
||||
// Store shape information in the tree control
|
||||
tree->setDataField(StringTable->insert("_nodeCount"), 0, avar("%d", stats.numNodes));
|
||||
tree->setDataField(StringTable->insert("_meshCount"), 0, avar("%d", stats.numMeshes));
|
||||
tree->setDataField(StringTable->insert("_polygonCount"), 0, avar("%d", stats.numPolygons));
|
||||
tree->setDataField(StringTable->insert("_materialCount"), 0, avar("%d", stats.numMaterials));
|
||||
tree->setDataField(StringTable->insert("_lightCount"), 0, avar("%d", stats.numLights));
|
||||
tree->setDataField(StringTable->insert("_animCount"), 0, avar("%d", stats.numClips));
|
||||
tree->setDataField(StringTable->insert("_unit"), 0, avar("%g", unit));
|
||||
|
||||
if (upAxis == UPAXISTYPE_X_UP)
|
||||
tree->setDataField(StringTable->insert("_upAxis"), 0, "X_AXIS");
|
||||
else if (upAxis == UPAXISTYPE_Y_UP)
|
||||
tree->setDataField(StringTable->insert("_upAxis"), 0, "Y_AXIS");
|
||||
else
|
||||
tree->setDataField(StringTable->insert("_upAxis"), 0, "Z_AXIS");
|
||||
|
||||
return true;
|
||||
}
|
||||
236
Engine/source/ts/collada/colladaLights.cpp
Normal file
236
Engine/source/ts/collada/colladaLights.cpp
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/collada/colladaUtils.h"
|
||||
#include "ts/collada/colladaAppNode.h"
|
||||
#include "ts/collada/colladaShapeLoader.h"
|
||||
|
||||
#include "T3D/pointLight.h"
|
||||
#include "T3D/spotLight.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Collada <light> elements are very similar, but are arranged as separate, unrelated
|
||||
// classes. These template functions are used to provide a simple way to access the
|
||||
// common elements.
|
||||
template<class T> static void resolveLightColor(T* light, ColorF& color)
|
||||
{
|
||||
if (light->getColor())
|
||||
{
|
||||
color.red = light->getColor()->getValue()[0];
|
||||
color.green = light->getColor()->getValue()[1];
|
||||
color.blue = light->getColor()->getValue()[2];
|
||||
}
|
||||
}
|
||||
|
||||
template<class T> static void resolveLightAttenuation(T* light, Point3F& attenuationRatio)
|
||||
{
|
||||
if (light->getConstant_attenuation())
|
||||
attenuationRatio.x = light->getConstant_attenuation()->getValue();
|
||||
if (light->getLinear_attenuation())
|
||||
attenuationRatio.y = light->getLinear_attenuation()->getValue();
|
||||
if (light->getQuadratic_attenuation())
|
||||
attenuationRatio.z = light->getQuadratic_attenuation()->getValue();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Recurse through the collada scene to add <light>s to the Torque scene
|
||||
static void processNodeLights(AppNode* appNode, const MatrixF& offset, SimGroup* group)
|
||||
{
|
||||
const domNode* node = dynamic_cast<ColladaAppNode*>(appNode)->getDomNode();
|
||||
|
||||
for (S32 iLight = 0; iLight < node->getInstance_light_array().getCount(); iLight++) {
|
||||
|
||||
domInstance_light* instLight = node->getInstance_light_array()[iLight];
|
||||
domLight* p_domLight = daeSafeCast<domLight>(instLight->getUrl().getElement());
|
||||
if (!p_domLight) {
|
||||
Con::warnf("Failed to find light for URL \"%s\"", instLight->getUrl().getOriginalURI());
|
||||
continue;
|
||||
}
|
||||
|
||||
String lightName = Sim::getUniqueName(_GetNameOrId(node));
|
||||
const char* lightType = "";
|
||||
|
||||
domLight::domTechnique_common* technique = p_domLight->getTechnique_common();
|
||||
if (!technique) {
|
||||
Con::warnf("No <technique_common> for light \"%s\"", lightName.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
LightBase* pLight = 0;
|
||||
ColorF color(ColorF::WHITE);
|
||||
Point3F attenuation(0, 1, 1);
|
||||
|
||||
if (technique->getAmbient()) {
|
||||
domLight::domTechnique_common::domAmbient* ambient = technique->getAmbient();
|
||||
// No explicit support for ambient lights, so use a PointLight instead
|
||||
lightType = "ambient";
|
||||
pLight = new PointLight;
|
||||
resolveLightColor(ambient, color);
|
||||
}
|
||||
else if (technique->getDirectional()) {
|
||||
domLight::domTechnique_common::domDirectional* directional = technique->getDirectional();
|
||||
// No explicit support for directional lights, so use a SpotLight instead
|
||||
lightType = "directional";
|
||||
pLight = new SpotLight;
|
||||
resolveLightColor(directional, color);
|
||||
}
|
||||
else if (technique->getPoint()) {
|
||||
domLight::domTechnique_common::domPoint* point = technique->getPoint();
|
||||
lightType = "point";
|
||||
pLight = new PointLight;
|
||||
resolveLightColor(point, color);
|
||||
resolveLightAttenuation(point, attenuation);
|
||||
}
|
||||
else if (technique->getSpot()) {
|
||||
domLight::domTechnique_common::domSpot* spot = technique->getSpot();
|
||||
lightType = "spot";
|
||||
pLight = new SpotLight;
|
||||
resolveLightColor(spot, color);
|
||||
resolveLightAttenuation(spot, attenuation);
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
Con::printf("Adding <%s> light \"%s\" as a %s", lightType, lightName.c_str(), pLight->getClassName());
|
||||
|
||||
MatrixF mat(offset);
|
||||
mat.mul(appNode->getNodeTransform(TSShapeLoader::DefaultTime));
|
||||
|
||||
pLight->setDataField(StringTable->insert("color"), 0,
|
||||
avar("%f %f %f %f", color.red, color.green, color.blue, color.alpha));
|
||||
pLight->setDataField(StringTable->insert("attenuationRatio"), 0,
|
||||
avar("%f %f %f", attenuation.x, attenuation.y, attenuation.z));
|
||||
pLight->setTransform(mat);
|
||||
|
||||
if (!pLight->registerObject(lightName)) {
|
||||
Con::errorf(ConsoleLogEntry::General, "Failed to register light for \"%s\"", lightName.c_str());
|
||||
delete pLight;
|
||||
}
|
||||
|
||||
if (group)
|
||||
group->addObject(pLight);
|
||||
}
|
||||
|
||||
// Recurse child nodes
|
||||
for (S32 iChild = 0; iChild < appNode->getNumChildNodes(); iChild++)
|
||||
processNodeLights(appNode->getChildNode(iChild), offset, group);
|
||||
}
|
||||
|
||||
// Load lights from a collada file and add to the scene.
|
||||
ConsoleFunction( loadColladaLights, bool, 2, 4,
|
||||
"(string filename, SimGroup parentGroup=MissionGroup, SimObject baseObject=-1)"
|
||||
"Load all light instances from a COLLADA (.dae) file and add to the scene.\n"
|
||||
"@param filename COLLADA filename to load lights from\n"
|
||||
"@param parentGroup (optional) name of an existing simgroup to add the new "
|
||||
"lights to (defaults to MissionGroup)\n"
|
||||
"@param baseObject (optional) name of an object to use as the origin (useful "
|
||||
"if you are loading the lights for a collada scene and have moved or rotated "
|
||||
"the geometry)\n"
|
||||
"@return true if successful, false otherwise\n\n"
|
||||
"@tsexample\n"
|
||||
"// load the lights in room.dae\n"
|
||||
"loadColladaLights( \"art/shapes/collada/room.dae\" );\n\n"
|
||||
"// load the lights in room.dae and add them to the RoomLights group\n"
|
||||
"loadColladaLights( \"art/shapes/collada/room.dae\", \"RoomLights\" );\n\n"
|
||||
"// load the lights in room.dae and use the transform of the \"Room\"\n"
|
||||
"object as the origin\n"
|
||||
"loadColladaLights( \"art/shapes/collada/room.dae\", \"\", \"Room\" );\n"
|
||||
"@endtsexample\n\n"
|
||||
"@note Currently for editor use only\n"
|
||||
"@ingroup Editors\n"
|
||||
"@internal")
|
||||
{
|
||||
Torque::Path path(argv[1]);
|
||||
|
||||
// Optional group to add the lights to. Create if it does not exist, and use
|
||||
// the MissionGroup if not specified.
|
||||
SimGroup* missionGroup = dynamic_cast<SimGroup*>(Sim::findObject("MissionGroup"));
|
||||
SimGroup* group = 0;
|
||||
if ((argc > 2) && (argv[2][0])) {
|
||||
if (!Sim::findObject(argv[2], group)) {
|
||||
// Create the group if it could not be found
|
||||
group = new SimGroup;
|
||||
if (group->registerObject(argv[2])) {
|
||||
if (missionGroup)
|
||||
missionGroup->addObject(group);
|
||||
}
|
||||
else {
|
||||
delete group;
|
||||
group = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!group)
|
||||
group = missionGroup;
|
||||
|
||||
// Optional object to provide the base transform
|
||||
MatrixF offset(true);
|
||||
if (argc > 3) {
|
||||
SceneObject *obj;
|
||||
if (Sim::findObject(argv[3], obj))
|
||||
offset = obj->getTransform();
|
||||
}
|
||||
|
||||
// Load the Collada file into memory
|
||||
domCOLLADA* root = ColladaShapeLoader::getDomCOLLADA(path);
|
||||
if (!root) {
|
||||
TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Load complete");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract the global scale and up_axis from the top level <asset> element,
|
||||
F32 unit = 1.0f;
|
||||
domUpAxisType upAxis = UPAXISTYPE_Z_UP;
|
||||
if (root->getAsset()) {
|
||||
if (root->getAsset()->getUnit())
|
||||
unit = root->getAsset()->getUnit()->getMeter();
|
||||
if (root->getAsset()->getUp_axis())
|
||||
upAxis = root->getAsset()->getUp_axis()->getValue();
|
||||
}
|
||||
|
||||
ColladaUtils::getOptions().unit = unit;
|
||||
ColladaUtils::getOptions().upAxis = upAxis;
|
||||
|
||||
// First grab all of the top-level nodes
|
||||
Vector<ColladaAppNode*> sceneNodes;
|
||||
for (int iSceneLib = 0; iSceneLib < root->getLibrary_visual_scenes_array().getCount(); iSceneLib++) {
|
||||
const domLibrary_visual_scenes* libScenes = root->getLibrary_visual_scenes_array()[iSceneLib];
|
||||
for (int iScene = 0; iScene < libScenes->getVisual_scene_array().getCount(); iScene++) {
|
||||
const domVisual_scene* visualScene = libScenes->getVisual_scene_array()[iScene];
|
||||
for (int iNode = 0; iNode < visualScene->getNode_array().getCount(); iNode++)
|
||||
sceneNodes.push_back(new ColladaAppNode(visualScene->getNode_array()[iNode]));
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse the scene tree looking for <instance_light>s
|
||||
for (S32 iNode = 0; iNode < sceneNodes.size(); iNode++) {
|
||||
processNodeLights(sceneNodes[iNode], offset, group);
|
||||
delete sceneNodes[iNode];
|
||||
}
|
||||
|
||||
TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Load complete");
|
||||
|
||||
return true;
|
||||
}
|
||||
725
Engine/source/ts/collada/colladaShapeLoader.cpp
Normal file
725
Engine/source/ts/collada/colladaShapeLoader.cpp
Normal file
|
|
@ -0,0 +1,725 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Resource stream -> Buffer
|
||||
Buffer -> Collada DOM
|
||||
Collada DOM -> TSShapeLoader
|
||||
TSShapeLoader installed into TSShape
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#include "ts/collada/colladaShapeLoader.h"
|
||||
#include "ts/collada/colladaUtils.h"
|
||||
#include "ts/collada/colladaAppNode.h"
|
||||
#include "ts/collada/colladaAppMesh.h"
|
||||
#include "ts/collada/colladaAppMaterial.h"
|
||||
#include "ts/collada/colladaAppSequence.h"
|
||||
|
||||
#include "core/util/tVector.h"
|
||||
#include "core/strings/findMatch.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "core/fileObject.h"
|
||||
#include "ts/tsShape.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "materials/materialManager.h"
|
||||
#include "console/persistenceManager.h"
|
||||
#include "ts/tsShapeConstruct.h"
|
||||
#include "core/util/zip/zipVolume.h"
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
|
||||
//
|
||||
static DAE sDAE; // Collada model database (holds the last loaded file)
|
||||
static Torque::Path sLastPath; // Path of the last loaded Collada file
|
||||
static FileTime sLastModTime; // Modification time of the last loaded Collada file
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Custom warning/error message handler
|
||||
class myErrorHandler : public daeErrorHandler
|
||||
{
|
||||
void handleError( daeString msg )
|
||||
{
|
||||
Con::errorf("Error: %s", msg);
|
||||
}
|
||||
|
||||
void handleWarning( daeString msg )
|
||||
{
|
||||
Con::errorf("Warning: %s", msg);
|
||||
}
|
||||
} sErrorHandler;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ColladaShapeLoader::ColladaShapeLoader(domCOLLADA* _root)
|
||||
: root(_root)
|
||||
{
|
||||
// Extract the global scale and up_axis from the top level <asset> element,
|
||||
F32 unit = 1.0f;
|
||||
domUpAxisType upAxis = UPAXISTYPE_Z_UP;
|
||||
if (root->getAsset()) {
|
||||
if (root->getAsset()->getUnit())
|
||||
unit = root->getAsset()->getUnit()->getMeter();
|
||||
if (root->getAsset()->getUp_axis())
|
||||
upAxis = root->getAsset()->getUp_axis()->getValue();
|
||||
}
|
||||
|
||||
// Set import options (if they are not set to override)
|
||||
if (ColladaUtils::getOptions().unit <= 0.0f)
|
||||
ColladaUtils::getOptions().unit = unit;
|
||||
|
||||
if (ColladaUtils::getOptions().upAxis == UPAXISTYPE_COUNT)
|
||||
ColladaUtils::getOptions().upAxis = upAxis;
|
||||
}
|
||||
|
||||
ColladaShapeLoader::~ColladaShapeLoader()
|
||||
{
|
||||
// Delete all of the animation channels
|
||||
for (int iAnim = 0; iAnim < animations.size(); iAnim++) {
|
||||
for (int iChannel = 0; iChannel < animations[iAnim]->size(); iChannel++)
|
||||
delete (*animations[iAnim])[iChannel];
|
||||
delete animations[iAnim];
|
||||
}
|
||||
animations.clear();
|
||||
}
|
||||
|
||||
void ColladaShapeLoader::processAnimation(const domAnimation* anim, F32& maxEndTime, F32& minFrameTime)
|
||||
{
|
||||
const char* sRGBANames[] = { ".R", ".G", ".B", ".A", "" };
|
||||
const char* sXYZNames[] = { ".X", ".Y", ".Z", "" };
|
||||
const char* sXYZANames[] = { ".X", ".Y", ".Z", ".ANGLE" };
|
||||
const char* sLOOKATNames[] = { ".POSITIONX", ".POSITIONY", ".POSITIONZ", ".TARGETX", ".TARGETY", ".TARGETZ", ".UPX", ".UPY", ".UPZ", "" };
|
||||
const char* sSKEWNames[] = { ".ROTATEX", ".ROTATEY", ".ROTATEZ", ".AROUNDX", ".AROUNDY", ".AROUNDZ", ".ANGLE", "" };
|
||||
const char* sNullNames[] = { "" };
|
||||
|
||||
for (int iChannel = 0; iChannel < anim->getChannel_array().getCount(); iChannel++) {
|
||||
|
||||
// Get the animation elements: <channel>, <sampler>
|
||||
domChannel* channel = anim->getChannel_array()[iChannel];
|
||||
domSampler* sampler = daeSafeCast<domSampler>(channel->getSource().getElement());
|
||||
if (!sampler)
|
||||
continue;
|
||||
|
||||
// Find the animation channel target
|
||||
daeSIDResolver resolver(channel, channel->getTarget());
|
||||
daeElement* target = resolver.getElement();
|
||||
if (!target) {
|
||||
daeErrorHandler::get()->handleWarning(avar("Failed to resolve animation "
|
||||
"target: %s", channel->getTarget()));
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
// If the target is a <source>, point it at the array instead
|
||||
// @todo:Only support targeting float arrays for now...
|
||||
if (target->getElementType() == COLLADA_TYPE::SOURCE)
|
||||
{
|
||||
domSource* source = daeSafeCast<domSource>(target);
|
||||
if (source->getFloat_array())
|
||||
target = source->getFloat_array();
|
||||
}
|
||||
*/
|
||||
// Get the target's animation channels (create them if not already)
|
||||
if (!AnimData::getAnimChannels(target)) {
|
||||
animations.push_back(new AnimChannels(target));
|
||||
}
|
||||
AnimChannels* targetChannels = AnimData::getAnimChannels(target);
|
||||
|
||||
// Add a new animation channel to the target
|
||||
targetChannels->push_back(new AnimData());
|
||||
channel->setUserData(targetChannels->last());
|
||||
AnimData& data = *targetChannels->last();
|
||||
|
||||
for (int iInput = 0; iInput < sampler->getInput_array().getCount(); iInput++) {
|
||||
|
||||
const domInputLocal* input = sampler->getInput_array()[iInput];
|
||||
const domSource* source = daeSafeCast<domSource>(input->getSource().getElement());
|
||||
if (!source)
|
||||
continue;
|
||||
|
||||
// @todo:don't care about the input param names for now. Could
|
||||
// validate against the target type....
|
||||
if (dStrEqual(input->getSemantic(), "INPUT")) {
|
||||
data.input.initFromSource(source);
|
||||
// Adjust the maximum sequence end time
|
||||
maxEndTime = getMax(maxEndTime, data.input.getFloatValue((S32)data.input.size()-1));
|
||||
|
||||
// Detect the frame rate (minimum time between keyframes)
|
||||
for (S32 iFrame = 1; iFrame < data.input.size(); iFrame++)
|
||||
{
|
||||
F32 delta = data.input.getFloatValue( iFrame ) - data.input.getFloatValue( iFrame-1 );
|
||||
if ( delta < 0 )
|
||||
{
|
||||
daeErrorHandler::get()->handleError(avar("<animation> INPUT '%s' "
|
||||
"has non-monotonic keys. Animation is unlikely to be imported correctly.", source->getID()));
|
||||
break;
|
||||
}
|
||||
minFrameTime = getMin( minFrameTime, delta );
|
||||
}
|
||||
}
|
||||
else if (dStrEqual(input->getSemantic(), "OUTPUT"))
|
||||
data.output.initFromSource(source);
|
||||
else if (dStrEqual(input->getSemantic(), "IN_TANGENT"))
|
||||
data.inTangent.initFromSource(source);
|
||||
else if (dStrEqual(input->getSemantic(), "OUT_TANGENT"))
|
||||
data.outTangent.initFromSource(source);
|
||||
else if (dStrEqual(input->getSemantic(), "INTERPOLATION"))
|
||||
data.interpolation.initFromSource(source);
|
||||
}
|
||||
|
||||
// Set initial value for visibility targets that were added automatically (in colladaUtils.cpp
|
||||
if (dStrEqual(target->getElementName(), "visibility"))
|
||||
{
|
||||
domAny* visTarget = daeSafeCast<domAny>(target);
|
||||
if (visTarget && dStrEqual(visTarget->getValue(), ""))
|
||||
visTarget->setValue(avar("%g", data.output.getFloatValue(0)));
|
||||
}
|
||||
|
||||
// Ignore empty animations
|
||||
if (data.input.size() == 0) {
|
||||
channel->setUserData(0);
|
||||
delete targetChannels->last();
|
||||
targetChannels->pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine the number and offset the elements of the target value
|
||||
// targeted by this animation
|
||||
switch (target->getElementType()) {
|
||||
case COLLADA_TYPE::COLOR: data.parseTargetString(channel->getTarget(), 4, sRGBANames); break;
|
||||
case COLLADA_TYPE::TRANSLATE: data.parseTargetString(channel->getTarget(), 3, sXYZNames); break;
|
||||
case COLLADA_TYPE::ROTATE: data.parseTargetString(channel->getTarget(), 4, sXYZANames); break;
|
||||
case COLLADA_TYPE::SCALE: data.parseTargetString(channel->getTarget(), 3, sXYZNames); break;
|
||||
case COLLADA_TYPE::LOOKAT: data.parseTargetString(channel->getTarget(), 3, sLOOKATNames); break;
|
||||
case COLLADA_TYPE::SKEW: data.parseTargetString(channel->getTarget(), 3, sSKEWNames); break;
|
||||
case COLLADA_TYPE::MATRIX: data.parseTargetString(channel->getTarget(), 16, sNullNames); break;
|
||||
case COLLADA_TYPE::FLOAT_ARRAY: data.parseTargetString(channel->getTarget(), daeSafeCast<domFloat_array>(target)->getCount(), sNullNames); break;
|
||||
default: data.parseTargetString(channel->getTarget(), 1, sNullNames); break;
|
||||
}
|
||||
}
|
||||
|
||||
// Process child animations
|
||||
for (int iAnim = 0; iAnim < anim->getAnimation_array().getCount(); iAnim++)
|
||||
processAnimation(anim->getAnimation_array()[iAnim], maxEndTime, minFrameTime);
|
||||
}
|
||||
|
||||
void ColladaShapeLoader::enumerateScene()
|
||||
{
|
||||
// Get animation clips
|
||||
Vector<const domAnimation_clip*> animationClips;
|
||||
for (int iClipLib = 0; iClipLib < root->getLibrary_animation_clips_array().getCount(); iClipLib++) {
|
||||
const domLibrary_animation_clips* libraryClips = root->getLibrary_animation_clips_array()[iClipLib];
|
||||
for (int iClip = 0; iClip < libraryClips->getAnimation_clip_array().getCount(); iClip++)
|
||||
appSequences.push_back(new ColladaAppSequence(libraryClips->getAnimation_clip_array()[iClip]));
|
||||
}
|
||||
|
||||
// Process all animations => this attaches animation channels to the targeted
|
||||
// Collada elements, and determines the length of the sequence if it is not
|
||||
// already specified in the Collada <animation_clip> element
|
||||
for (int iSeq = 0; iSeq < appSequences.size(); iSeq++) {
|
||||
ColladaAppSequence* appSeq = dynamic_cast<ColladaAppSequence*>(appSequences[iSeq]);
|
||||
F32 maxEndTime = 0;
|
||||
F32 minFrameTime = 1000.0f;
|
||||
for (int iAnim = 0; iAnim < appSeq->getClip()->getInstance_animation_array().getCount(); iAnim++) {
|
||||
domAnimation* anim = daeSafeCast<domAnimation>(appSeq->getClip()->getInstance_animation_array()[iAnim]->getUrl().getElement());
|
||||
if (anim)
|
||||
processAnimation(anim, maxEndTime, minFrameTime);
|
||||
}
|
||||
if (appSeq->getEnd() == 0)
|
||||
appSeq->setEnd(maxEndTime);
|
||||
|
||||
// Collada animations can be stored as sampled frames or true keyframes. For
|
||||
// sampled frames, use the same frame rate as the DAE file. For true keyframes,
|
||||
// resample at a fixed frame rate.
|
||||
appSeq->fps = mClamp(1.0f / minFrameTime + 0.5f, TSShapeLoader::MinFrameRate, TSShapeLoader::MaxFrameRate);
|
||||
}
|
||||
|
||||
// First grab all of the top-level nodes
|
||||
Vector<domNode*> sceneNodes;
|
||||
for (int iSceneLib = 0; iSceneLib < root->getLibrary_visual_scenes_array().getCount(); iSceneLib++) {
|
||||
const domLibrary_visual_scenes* libScenes = root->getLibrary_visual_scenes_array()[iSceneLib];
|
||||
for (int iScene = 0; iScene < libScenes->getVisual_scene_array().getCount(); iScene++) {
|
||||
const domVisual_scene* visualScene = libScenes->getVisual_scene_array()[iScene];
|
||||
for (int iNode = 0; iNode < visualScene->getNode_array().getCount(); iNode++)
|
||||
sceneNodes.push_back(visualScene->getNode_array()[iNode]);
|
||||
}
|
||||
}
|
||||
|
||||
// Set LOD option
|
||||
bool singleDetail = true;
|
||||
switch (ColladaUtils::getOptions().lodType)
|
||||
{
|
||||
case ColladaUtils::ImportOptions::DetectDTS:
|
||||
// Check for a baseXX->startXX hierarchy at the top-level, if we find
|
||||
// one, use trailing numbers for LOD, otherwise use a single size
|
||||
for (int iNode = 0; singleDetail && (iNode < sceneNodes.size()); iNode++) {
|
||||
domNode* node = sceneNodes[iNode];
|
||||
if (dStrStartsWith(_GetNameOrId(node), "base")) {
|
||||
for (int iChild = 0; iChild < node->getNode_array().getCount(); iChild++) {
|
||||
domNode* child = node->getNode_array()[iChild];
|
||||
if (dStrStartsWith(_GetNameOrId(child), "start")) {
|
||||
singleDetail = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ColladaUtils::ImportOptions::SingleSize:
|
||||
singleDetail = true;
|
||||
break;
|
||||
|
||||
case ColladaUtils::ImportOptions::TrailingNumber:
|
||||
singleDetail = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ColladaAppMesh::fixDetailSize( singleDetail, ColladaUtils::getOptions().singleDetailSize );
|
||||
|
||||
// Process the top level nodes
|
||||
for (S32 iNode = 0; iNode < sceneNodes.size(); iNode++) {
|
||||
ColladaAppNode* node = new ColladaAppNode(sceneNodes[iNode], 0);
|
||||
if (!processNode(node))
|
||||
delete node;
|
||||
}
|
||||
|
||||
// Make sure that the scene has a bounds node (for getting the root scene transform)
|
||||
if (!boundsNode)
|
||||
{
|
||||
domVisual_scene* visualScene = root->getLibrary_visual_scenes_array()[0]->getVisual_scene_array()[0];
|
||||
domNode* dombounds = daeSafeCast<domNode>( visualScene->createAndPlace( "node" ) );
|
||||
dombounds->setName( "bounds" );
|
||||
ColladaAppNode *appBounds = new ColladaAppNode(dombounds, 0);
|
||||
if (!processNode(appBounds))
|
||||
delete appBounds;
|
||||
}
|
||||
}
|
||||
|
||||
bool ColladaShapeLoader::ignoreNode(const String& name)
|
||||
{
|
||||
if (FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().alwaysImport, name, false))
|
||||
return false;
|
||||
else
|
||||
return FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().neverImport, name, false);
|
||||
}
|
||||
|
||||
bool ColladaShapeLoader::ignoreMesh(const String& name)
|
||||
{
|
||||
if (FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().alwaysImportMesh, name, false))
|
||||
return false;
|
||||
else
|
||||
return FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().neverImportMesh, name, false);
|
||||
}
|
||||
|
||||
void ColladaShapeLoader::computeBounds(Box3F& bounds)
|
||||
{
|
||||
TSShapeLoader::computeBounds(bounds);
|
||||
|
||||
// Check if the model origin needs adjusting
|
||||
if ( bounds.isValidBox() &&
|
||||
(ColladaUtils::getOptions().adjustCenter ||
|
||||
ColladaUtils::getOptions().adjustFloor) )
|
||||
{
|
||||
// Compute shape offset
|
||||
Point3F shapeOffset = Point3F::Zero;
|
||||
if ( ColladaUtils::getOptions().adjustCenter )
|
||||
{
|
||||
bounds.getCenter( &shapeOffset );
|
||||
shapeOffset = -shapeOffset;
|
||||
}
|
||||
if ( ColladaUtils::getOptions().adjustFloor )
|
||||
shapeOffset.z = -bounds.minExtents.z;
|
||||
|
||||
// Adjust bounds
|
||||
bounds.minExtents += shapeOffset;
|
||||
bounds.maxExtents += shapeOffset;
|
||||
|
||||
// Now adjust all positions for root level nodes (nodes with no parent)
|
||||
for (S32 iNode = 0; iNode < shape->nodes.size(); iNode++)
|
||||
{
|
||||
if ( !appNodes[iNode]->isParentRoot() )
|
||||
continue;
|
||||
|
||||
// Adjust default translation
|
||||
shape->defaultTranslations[iNode] += shapeOffset;
|
||||
|
||||
// Adjust animated translations
|
||||
for (S32 iSeq = 0; iSeq < shape->sequences.size(); iSeq++)
|
||||
{
|
||||
const TSShape::Sequence& seq = shape->sequences[iSeq];
|
||||
if ( seq.translationMatters.test(iNode) )
|
||||
{
|
||||
for (S32 iFrame = 0; iFrame < seq.numKeyframes; iFrame++)
|
||||
{
|
||||
S32 index = seq.baseTranslation + seq.translationMatters.count(iNode)*seq.numKeyframes + iFrame;
|
||||
shape->nodeTranslations[index] += shapeOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Find the file extension for an extensionless texture
|
||||
String findTextureExtension(const Torque::Path &texPath)
|
||||
{
|
||||
Torque::Path path(texPath);
|
||||
for(S32 i = 0;i < GBitmap::sRegistrations.size();++i)
|
||||
{
|
||||
GBitmap::Registration ® = GBitmap::sRegistrations[i];
|
||||
for(S32 j = 0;j < reg.extensions.size();++j)
|
||||
{
|
||||
path.setExtension(reg.extensions[j]);
|
||||
if (Torque::FS::IsFile(path))
|
||||
return path.getExtension();
|
||||
}
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Copy a texture from a KMZ to a cache. Note that the texture filename is modified
|
||||
void copySketchupTexture(const Torque::Path &path, String &textureFilename)
|
||||
{
|
||||
if (textureFilename.isEmpty())
|
||||
return;
|
||||
|
||||
Torque::Path texturePath(textureFilename);
|
||||
texturePath.setExtension(findTextureExtension(texturePath));
|
||||
|
||||
String cachedTexFilename = String::ToString("%s_%s.cached",
|
||||
TSShapeLoader::getShapePath().getFileName().c_str(), texturePath.getFileName().c_str());
|
||||
|
||||
Torque::Path cachedTexPath;
|
||||
cachedTexPath.setRoot(path.getRoot());
|
||||
cachedTexPath.setPath(path.getPath());
|
||||
cachedTexPath.setFileName(cachedTexFilename);
|
||||
cachedTexPath.setExtension(texturePath.getExtension());
|
||||
|
||||
FileStream *source;
|
||||
FileStream *dest;
|
||||
if ((source = FileStream::createAndOpen(texturePath.getFullPath(), Torque::FS::File::Read)) == NULL)
|
||||
return;
|
||||
|
||||
if ((dest = FileStream::createAndOpen(cachedTexPath.getFullPath(), Torque::FS::File::Write)) == NULL)
|
||||
{
|
||||
delete source;
|
||||
return;
|
||||
}
|
||||
|
||||
dest->copyFrom(source);
|
||||
|
||||
delete dest;
|
||||
delete source;
|
||||
|
||||
// Update the filename in the material
|
||||
cachedTexPath.setExtension("");
|
||||
textureFilename = cachedTexPath.getFullPath();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Add collada materials to materials.cs
|
||||
void updateMaterialsScript(const Torque::Path &path, bool copyTextures = false)
|
||||
{
|
||||
#ifdef DAE2DTS_TOOL
|
||||
if (!ColladaUtils::getOptions().forceUpdateMaterials)
|
||||
return;
|
||||
#endif
|
||||
|
||||
Torque::Path scriptPath(path);
|
||||
scriptPath.setFileName("materials");
|
||||
scriptPath.setExtension("cs");
|
||||
|
||||
// First see what materials we need to update
|
||||
PersistenceManager persistMgr;
|
||||
for ( U32 iMat = 0; iMat < AppMesh::appMaterials.size(); iMat++ )
|
||||
{
|
||||
ColladaAppMaterial *mat = dynamic_cast<ColladaAppMaterial*>( AppMesh::appMaterials[iMat] );
|
||||
if ( mat )
|
||||
{
|
||||
Material *mappedMat;
|
||||
if ( Sim::findObject( MATMGR->getMapEntry( mat->getName() ), mappedMat ) )
|
||||
{
|
||||
// Only update existing materials if forced to
|
||||
if ( ColladaUtils::getOptions().forceUpdateMaterials )
|
||||
persistMgr.setDirty( mappedMat );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new material definition
|
||||
persistMgr.setDirty( mat->createMaterial( scriptPath ), scriptPath.getFullPath() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( persistMgr.getDirtyList().empty() )
|
||||
return;
|
||||
|
||||
// If importing a sketchup file, the paths will point inside the KMZ so we need to cache them.
|
||||
if (copyTextures)
|
||||
{
|
||||
for (int iMat = 0; iMat < persistMgr.getDirtyList().size(); iMat++)
|
||||
{
|
||||
Material *mat = dynamic_cast<Material*>( persistMgr.getDirtyList()[iMat].getObject() );
|
||||
|
||||
copySketchupTexture(path, mat->mDiffuseMapFilename[0]);
|
||||
copySketchupTexture(path, mat->mNormalMapFilename[0]);
|
||||
copySketchupTexture(path, mat->mSpecularMapFilename[0]);
|
||||
}
|
||||
}
|
||||
|
||||
persistMgr.saveDirty();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Check if an up-to-date cached DTS is available for this DAE file
|
||||
bool ColladaShapeLoader::canLoadCachedDTS(const Torque::Path& path)
|
||||
{
|
||||
// Generate the cached filename
|
||||
Torque::Path cachedPath(path);
|
||||
cachedPath.setExtension("cached.dts");
|
||||
|
||||
// Check if a cached DTS newer than this file is available
|
||||
FileTime cachedModifyTime;
|
||||
if (Platform::getFileTimes(cachedPath.getFullPath(), NULL, &cachedModifyTime))
|
||||
{
|
||||
bool forceLoadDAE = Con::getBoolVariable("$collada::forceLoadDAE", false);
|
||||
|
||||
FileTime daeModifyTime;
|
||||
if (!Platform::getFileTimes(path.getFullPath(), NULL, &daeModifyTime) ||
|
||||
(!forceLoadDAE && (Platform::compareFileTimes(cachedModifyTime, daeModifyTime) >= 0) ))
|
||||
{
|
||||
// DAE not found, or cached DTS is newer
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ColladaShapeLoader::checkAndMountSketchup(const Torque::Path& path, String& mountPoint, Torque::Path& daePath)
|
||||
{
|
||||
bool isSketchup = path.getExtension().equal("kmz", String::NoCase);
|
||||
if (isSketchup)
|
||||
{
|
||||
// Mount the zip so files can be found (it will be unmounted before we return)
|
||||
mountPoint = String("sketchup_") + path.getFileName();
|
||||
String zipPath = path.getFullPath();
|
||||
if (!Torque::FS::Mount(mountPoint, new Torque::ZipFileSystem(zipPath)))
|
||||
return false;
|
||||
|
||||
Vector<String> daeFiles;
|
||||
Torque::Path findPath;
|
||||
findPath.setRoot(mountPoint);
|
||||
S32 results = Torque::FS::FindByPattern(findPath, "*.dae", true, daeFiles);
|
||||
if (results == 0 || daeFiles.size() == 0)
|
||||
{
|
||||
Torque::FS::Unmount(mountPoint);
|
||||
return false;
|
||||
}
|
||||
|
||||
daePath = daeFiles[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
daePath = path;
|
||||
}
|
||||
|
||||
return isSketchup;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Get the root collada DOM element for the given DAE file
|
||||
domCOLLADA* ColladaShapeLoader::getDomCOLLADA(const Torque::Path& path)
|
||||
{
|
||||
daeErrorHandler::setErrorHandler(&sErrorHandler);
|
||||
|
||||
TSShapeLoader::updateProgress(TSShapeLoader::Load_ReadFile, path.getFullFileName().c_str());
|
||||
|
||||
// Check if we can use the last loaded file
|
||||
FileTime daeModifyTime;
|
||||
if (Platform::getFileTimes(path.getFullPath(), NULL, &daeModifyTime))
|
||||
{
|
||||
if ((path == sLastPath) && (Platform::compareFileTimes(sLastModTime, daeModifyTime) >= 0))
|
||||
return sDAE.getRoot(path.getFullPath().c_str());
|
||||
}
|
||||
|
||||
sDAE.clear();
|
||||
sDAE.setBaseURI("");
|
||||
|
||||
TSShapeLoader::updateProgress(TSShapeLoader::Load_ParseFile, "Parsing XML...");
|
||||
domCOLLADA* root = readColladaFile(path.getFullPath());
|
||||
if (!root)
|
||||
{
|
||||
TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Import failed");
|
||||
sDAE.clear();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sLastPath = path;
|
||||
sLastModTime = daeModifyTime;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
domCOLLADA* ColladaShapeLoader::readColladaFile(const String& path)
|
||||
{
|
||||
// Check if this file is already loaded into the database
|
||||
domCOLLADA* root = sDAE.getRoot(path.c_str());
|
||||
if (root)
|
||||
return root;
|
||||
|
||||
// Load the Collada file into memory
|
||||
FileObject fo;
|
||||
if (!fo.readMemory(path))
|
||||
{
|
||||
daeErrorHandler::get()->handleError(avar("Could not read %s into memory", path.c_str()));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
root = sDAE.openFromMemory(path.c_str(), (const char*)fo.buffer());
|
||||
if (!root || !root->getLibrary_visual_scenes_array().getCount()) {
|
||||
daeErrorHandler::get()->handleError(avar("Could not parse %s", path.c_str()));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Fixup issues in the model
|
||||
ColladaUtils::applyConditioners(root);
|
||||
|
||||
// Recursively load external DAE references
|
||||
TSShapeLoader::updateProgress(TSShapeLoader::Load_ExternalRefs, "Loading external references...");
|
||||
for (S32 iRef = 0; iRef < root->getDocument()->getReferencedDocuments().getCount(); iRef++) {
|
||||
String refPath = (daeString)root->getDocument()->getReferencedDocuments()[iRef];
|
||||
if (refPath.endsWith(".dae") && !readColladaFile(refPath))
|
||||
daeErrorHandler::get()->handleError(avar("Failed to load external reference: %s", refPath.c_str()));
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// This function is invoked by the resource manager based on file extension.
|
||||
TSShape* loadColladaShape(const Torque::Path &path)
|
||||
{
|
||||
#ifndef DAE2DTS_TOOL
|
||||
// Generate the cached filename
|
||||
Torque::Path cachedPath(path);
|
||||
cachedPath.setExtension("cached.dts");
|
||||
|
||||
// Check if an up-to-date cached DTS version of this file exists, and
|
||||
// if so, use that instead.
|
||||
if (ColladaShapeLoader::canLoadCachedDTS(path))
|
||||
{
|
||||
FileStream cachedStream;
|
||||
cachedStream.open(cachedPath.getFullPath(), Torque::FS::File::Read);
|
||||
if (cachedStream.getStatus() == Stream::Ok)
|
||||
{
|
||||
TSShape *shape = new TSShape;
|
||||
bool readSuccess = shape->read(&cachedStream);
|
||||
cachedStream.close();
|
||||
|
||||
if (readSuccess)
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
Con::printf("Loaded cached Collada shape from %s", cachedPath.getFullPath().c_str());
|
||||
#endif
|
||||
return shape;
|
||||
}
|
||||
else
|
||||
delete shape;
|
||||
}
|
||||
|
||||
Con::warnf("Failed to load cached COLLADA shape from %s", cachedPath.getFullPath().c_str());
|
||||
}
|
||||
#endif // DAE2DTS_TOOL
|
||||
|
||||
if (!Torque::FS::IsFile(path))
|
||||
{
|
||||
// DAE file does not exist, bail.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef DAE2DTS_TOOL
|
||||
ColladaUtils::ImportOptions cmdLineOptions = ColladaUtils::getOptions();
|
||||
#endif
|
||||
|
||||
// Allow TSShapeConstructor object to override properties
|
||||
ColladaUtils::getOptions().reset();
|
||||
TSShapeConstructor* tscon = TSShapeConstructor::findShapeConstructor(path.getFullPath());
|
||||
if (tscon)
|
||||
{
|
||||
ColladaUtils::getOptions() = tscon->mOptions;
|
||||
|
||||
#ifdef DAE2DTS_TOOL
|
||||
// Command line overrides certain options
|
||||
ColladaUtils::getOptions().forceUpdateMaterials = cmdLineOptions.forceUpdateMaterials;
|
||||
ColladaUtils::getOptions().useDiffuseNames = cmdLineOptions.useDiffuseNames;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Check if this is a Sketchup file (.kmz) and if so, mount the zip filesystem
|
||||
// and get the path to the DAE file.
|
||||
String mountPoint;
|
||||
Torque::Path daePath;
|
||||
bool isSketchup = ColladaShapeLoader::checkAndMountSketchup(path, mountPoint, daePath);
|
||||
|
||||
// Load Collada model and convert to 3space
|
||||
TSShape* tss = 0;
|
||||
domCOLLADA* root = ColladaShapeLoader::getDomCOLLADA(daePath);
|
||||
if (root)
|
||||
{
|
||||
ColladaShapeLoader loader(root);
|
||||
tss = loader.generateShape(daePath);
|
||||
if (tss)
|
||||
{
|
||||
#ifndef DAE2DTS_TOOL
|
||||
// Cache the Collada model to a DTS file for faster loading next time.
|
||||
FileStream dtsStream;
|
||||
if (dtsStream.open(cachedPath.getFullPath(), Torque::FS::File::Write))
|
||||
{
|
||||
Con::printf("Writing cached COLLADA shape to %s", cachedPath.getFullPath().c_str());
|
||||
tss->write(&dtsStream);
|
||||
}
|
||||
#endif // DAE2DTS_TOOL
|
||||
|
||||
// Add collada materials to materials.cs
|
||||
updateMaterialsScript(path, isSketchup);
|
||||
}
|
||||
}
|
||||
|
||||
// Close progress dialog
|
||||
TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Import complete");
|
||||
|
||||
if (isSketchup)
|
||||
{
|
||||
// Unmount the zip if we mounted it
|
||||
Torque::FS::Unmount(mountPoint);
|
||||
}
|
||||
|
||||
return tss;
|
||||
}
|
||||
61
Engine/source/ts/collada/colladaShapeLoader.h
Normal file
61
Engine/source/ts/collada/colladaShapeLoader.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _COLLADA_SHAPELOADER_H_
|
||||
#define _COLLADA_SHAPELOADER_H_
|
||||
|
||||
#ifndef _TSSHAPELOADER_H_
|
||||
#include "ts/loader/tsShapeLoader.h"
|
||||
#endif
|
||||
|
||||
class domCOLLADA;
|
||||
class domAnimation;
|
||||
struct AnimChannels;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class ColladaShapeLoader : public TSShapeLoader
|
||||
{
|
||||
friend TSShape* loadColladaShape(const Torque::Path &path);
|
||||
|
||||
domCOLLADA* root;
|
||||
Vector<AnimChannels*> animations; ///< Holds all animation channels for deletion after loading
|
||||
|
||||
void processAnimation(const domAnimation* anim, F32& maxEndTime, F32& minFrameTime);
|
||||
|
||||
void cleanup();
|
||||
|
||||
public:
|
||||
ColladaShapeLoader(domCOLLADA* _root);
|
||||
~ColladaShapeLoader();
|
||||
|
||||
void enumerateScene();
|
||||
bool ignoreNode(const String& name);
|
||||
bool ignoreMesh(const String& name);
|
||||
void computeBounds(Box3F& bounds);
|
||||
|
||||
static bool canLoadCachedDTS(const Torque::Path& path);
|
||||
static bool checkAndMountSketchup(const Torque::Path& path, String& mountPoint, Torque::Path& daePath);
|
||||
static domCOLLADA* getDomCOLLADA(const Torque::Path& path);
|
||||
static domCOLLADA* readColladaFile(const String& path);
|
||||
};
|
||||
|
||||
#endif // _COLLADA_SHAPELOADER_H_
|
||||
1495
Engine/source/ts/collada/colladaUtils.cpp
Normal file
1495
Engine/source/ts/collada/colladaUtils.cpp
Normal file
File diff suppressed because it is too large
Load diff
810
Engine/source/ts/collada/colladaUtils.h
Normal file
810
Engine/source/ts/collada/colladaUtils.h
Normal file
|
|
@ -0,0 +1,810 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _COLLADA_UTILS_H_
|
||||
#define _COLLADA_UTILS_H_
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4786) // disable warning about long debug symbol names
|
||||
#pragma warning(disable : 4355) // disable "'this' : used in base member initializer list" warnings
|
||||
#endif
|
||||
|
||||
#ifndef _MMATRIX_H_
|
||||
#include "math/mMatrix.h"
|
||||
#endif
|
||||
#ifndef _MQUAT_H_
|
||||
#include "math/mQuat.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPE_LOADER_H_
|
||||
#include "ts/loader/tsShapeLoader.h"
|
||||
#endif
|
||||
#ifndef _OPTIMIZEDPOLYLIST_H_
|
||||
#include "collision/optimizedPolyList.h"
|
||||
#endif
|
||||
#ifndef TINYXML_INCLUDED
|
||||
#include "tinyxml.h"
|
||||
#endif
|
||||
#ifndef _CONSOLE_H_
|
||||
#include "console/console.h"
|
||||
#endif
|
||||
|
||||
#include "platform/tmm_off.h"
|
||||
|
||||
#include "dae.h"
|
||||
#include "dae/daeErrorHandler.h"
|
||||
#include "dae/domAny.h"
|
||||
#include "dom/domProfile_COMMON.h"
|
||||
#include "dom/domMaterial.h"
|
||||
#include "dom/domGeometry.h"
|
||||
#include "dom/domMorph.h"
|
||||
#include "dom/domNode.h"
|
||||
#include "dom/domCOLLADA.h"
|
||||
|
||||
#include "platform/tmm_on.h"
|
||||
|
||||
namespace ColladaUtils
|
||||
{
|
||||
struct ImportOptions
|
||||
{
|
||||
enum eLodType
|
||||
{
|
||||
DetectDTS = 0,
|
||||
SingleSize,
|
||||
TrailingNumber,
|
||||
NumLodTypes
|
||||
};
|
||||
|
||||
domUpAxisType upAxis; // Override for the collada <up_axis> element
|
||||
F32 unit; // Override for the collada <unit> element
|
||||
eLodType lodType; // LOD type option
|
||||
S32 singleDetailSize; // Detail size for all meshes in the model
|
||||
String matNamePrefix; // Prefix to apply to collada material names
|
||||
String alwaysImport; // List of node names (with wildcards) to import, even if in the neverImport list
|
||||
String neverImport; // List of node names (with wildcards) to ignore on loading
|
||||
String alwaysImportMesh; // List of mesh names (with wildcards) to import, even if in the neverImportMesh list
|
||||
String neverImportMesh; // List of mesh names (with wildcards) to ignore on loading
|
||||
bool ignoreNodeScale; // Ignore <scale> elements in <node>s
|
||||
bool adjustCenter; // Translate model so origin is at the center
|
||||
bool adjustFloor; // Translate model so origin is at the bottom
|
||||
bool forceUpdateMaterials; // Force update of materials.cs
|
||||
bool useDiffuseNames; // Use diffuse texture as the material name
|
||||
|
||||
ImportOptions()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
upAxis = UPAXISTYPE_COUNT;
|
||||
unit = -1.0f;
|
||||
lodType = DetectDTS;
|
||||
singleDetailSize = 2;
|
||||
matNamePrefix = "";
|
||||
alwaysImport = "";
|
||||
neverImport = "";
|
||||
alwaysImportMesh = "";
|
||||
neverImportMesh = "";
|
||||
ignoreNodeScale = false;
|
||||
adjustCenter = false;
|
||||
adjustFloor = false;
|
||||
forceUpdateMaterials = false;
|
||||
useDiffuseNames = false;
|
||||
}
|
||||
};
|
||||
|
||||
ImportOptions& getOptions();
|
||||
|
||||
void convertTransform(MatrixF& m);
|
||||
|
||||
void collapsePath(std::string& path);
|
||||
|
||||
// Apply the set of Collada conditioners (suited for loading Collada models into Torque)
|
||||
void applyConditioners(domCOLLADA* root);
|
||||
|
||||
const domProfile_COMMON* findEffectCommonProfile(const domEffect* effect);
|
||||
const domCommon_color_or_texture_type_complexType* findEffectDiffuse(const domEffect* effect);
|
||||
const domCommon_color_or_texture_type_complexType* findEffectSpecular(const domEffect* effect);
|
||||
const domFx_sampler2D_common_complexType* getTextureSampler(const domEffect* effect, const domCommon_color_or_texture_type_complexType* texture);
|
||||
String getSamplerImagePath(const domEffect* effect, const domFx_sampler2D_common_complexType* sampler2D);
|
||||
String resolveImagePath(const domImage* image);
|
||||
|
||||
// Collada export helper functions
|
||||
Torque::Path findTexture(const Torque::Path& diffuseMap);
|
||||
void exportColladaHeader(TiXmlElement* rootNode);
|
||||
void exportColladaMaterials(TiXmlElement* rootNode, const OptimizedPolyList& mesh, Vector<String>& matNames, const Torque::Path& colladaFile);
|
||||
void exportColladaTriangles(TiXmlElement* meshNode, const OptimizedPolyList& mesh, const String& meshName, const Vector<String>& matNames);
|
||||
void exportColladaMesh(TiXmlElement* rootNode, const OptimizedPolyList& mesh, const String& meshName, const Vector<String>& matNames);
|
||||
void exportColladaScene(TiXmlElement* rootNode, const String& meshName, const Vector<String>& matNames);
|
||||
|
||||
// Export an OptimizedPolyList to a simple Collada file
|
||||
void exportToCollada(const Torque::Path& colladaFile, const OptimizedPolyList& mesh, const String& meshName = String::EmptyString);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helper Classes
|
||||
//
|
||||
// The Collada DOM uses a different class for each XML element, and there is very
|
||||
// little class inheritance, even though many elements have the same attributes
|
||||
// and children. This makes the DOM a bit ugly to work with, and the following
|
||||
// templates attempt to make this situation a bit nicer by providing a common way
|
||||
// to access common elements, while retaining the strong typing of the DOM classes.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// Convert from the Collada transform types to a Torque MatrixF
|
||||
template<class T> inline MatrixF vecToMatrixF(const domListOfFloats& vec) { return MatrixF(true); }
|
||||
|
||||
/// Collada <translate>: [x_translate, y_translate, z_translate]
|
||||
template<> inline MatrixF vecToMatrixF<domTranslate>(const domListOfFloats& vec)
|
||||
{
|
||||
MatrixF mat(true);
|
||||
mat.setPosition(Point3F(vec[0], vec[1], vec[2]));
|
||||
return mat;
|
||||
}
|
||||
|
||||
/// Collada <scale>: [x_scale, y_scale, z_scale]
|
||||
template<> inline MatrixF vecToMatrixF<domScale>(const domListOfFloats& vec)
|
||||
{
|
||||
MatrixF mat(true);
|
||||
mat.scale(Point3F(vec[0], vec[1], vec[2]));
|
||||
return mat;
|
||||
}
|
||||
|
||||
/// Collada <rotate>: [rotation_axis, angle_in_degrees]
|
||||
template<> inline MatrixF vecToMatrixF<domRotate>(const domListOfFloats& vec)
|
||||
{
|
||||
AngAxisF aaxis(Point3F(vec[0], vec[1], vec[2]), -(vec[3] * M_PI) / 180.0f);
|
||||
MatrixF mat(true);
|
||||
aaxis.setMatrix(&mat);
|
||||
return mat;
|
||||
}
|
||||
|
||||
/// Collada <matrix>: same form as TGE (woohoo!)
|
||||
template<> inline MatrixF vecToMatrixF<domMatrix>(const domListOfFloats& vec)
|
||||
{
|
||||
MatrixF mat;
|
||||
for (int i = 0; i < 16; i++)
|
||||
mat[i] = vec[i];
|
||||
return mat;
|
||||
}
|
||||
|
||||
/// Collada <skew>: [angle_in_degrees, rotation_axis, translation_axis]
|
||||
/// skew transform code adapted from GMANMatrix4 implementation
|
||||
template<> inline MatrixF vecToMatrixF<domSkew>(const domListOfFloats& vec)
|
||||
{
|
||||
F32 angle = -(vec[0] * M_PI) / 180.0f;
|
||||
Point3F rotAxis(vec[1], vec[2], vec[3]);
|
||||
Point3F transAxis(vec[4], vec[5], vec[6]);
|
||||
|
||||
transAxis.normalize();
|
||||
|
||||
Point3F a1 = transAxis * mDot(rotAxis, transAxis);
|
||||
Point3F a2 = rotAxis - a1;
|
||||
a2.normalize();
|
||||
|
||||
F32 an1 = mDot(rotAxis, a2);
|
||||
F32 an2 = mDot(rotAxis, transAxis);
|
||||
|
||||
F32 rx = an1 * mCos(angle) - an2 * mSin(angle);
|
||||
F32 ry = an1 * mSin(angle) + an2 * mCos(angle);
|
||||
|
||||
// Check for rotation parallel to translation
|
||||
F32 alpha = (an1 == 0) ? 0 : (ry/rx - an2/an1);
|
||||
|
||||
MatrixF mat(true);
|
||||
mat(0,0) = a2.x * transAxis.x * alpha + 1.0;
|
||||
mat(1,0) = a2.y * transAxis.x * alpha;
|
||||
mat(2,0) = a2.z * transAxis.x * alpha;
|
||||
|
||||
mat(0,1) = a2.x * transAxis.y * alpha;
|
||||
mat(1,1) = a2.y * transAxis.y * alpha + 1.0;
|
||||
mat(2,1) = a2.z * transAxis.y * alpha;
|
||||
|
||||
mat(0,2) = a2.x * transAxis.z * alpha;
|
||||
mat(1,2) = a2.y * transAxis.z * alpha;
|
||||
mat(2,2) = a2.z * transAxis.z * alpha + 1.0;
|
||||
return mat;
|
||||
}
|
||||
|
||||
/// Collada <lookat>: [eye, target, up]
|
||||
template<> inline MatrixF vecToMatrixF<domLookat>(const domListOfFloats& vec)
|
||||
{
|
||||
Point3F eye(vec[0], vec[1], vec[2]);
|
||||
Point3F target(vec[3], vec[4], vec[5]);
|
||||
Point3F up(vec[6], vec[7], vec[8]);
|
||||
|
||||
Point3F fwd = target - eye;
|
||||
fwd.normalizeSafe();
|
||||
|
||||
Point3F right = mCross(fwd, up);
|
||||
right.normalizeSafe();
|
||||
|
||||
up = mCross(right, fwd);
|
||||
up.normalizeSafe();
|
||||
|
||||
MatrixF mat(true);
|
||||
mat.setColumn(0, right);
|
||||
mat.setColumn(1, fwd);
|
||||
mat.setColumn(2, up);
|
||||
mat.setColumn(3, eye);
|
||||
return mat;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// Try to get a name for the element using the following attributes (in order):
|
||||
/// name, sid, id, "null"
|
||||
template<class T> inline const char* _GetNameOrId(const T* element)
|
||||
{
|
||||
return element ? (element->getName() ? element->getName() : (element->getId() ? element->getId() : "null")) : "null";
|
||||
}
|
||||
|
||||
template<> inline const char* _GetNameOrId(const domInstance_geometry* element)
|
||||
{
|
||||
return element ? (element->getName() ? element->getName() : (element->getSid() ? element->getSid() : "null")) : "null";
|
||||
}
|
||||
|
||||
template<> inline const char* _GetNameOrId(const domInstance_controller* element)
|
||||
{
|
||||
return element ? (element->getName() ? element->getName() : (element->getSid() ? element->getSid() : "null")) : "null";
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Collada <source>s are extremely flexible, and thus difficult to access in a nice
|
||||
// way. This class attempts to provide a clean interface to convert Collada source
|
||||
// data to the appropriate Torque data structure without losing any of the flexibility
|
||||
// of the underlying Collada DOM.
|
||||
//
|
||||
// Some of the conversions we need to handle are:
|
||||
// - daeString to const char*
|
||||
// - daeIDRef to const char*
|
||||
// - double to F32
|
||||
// - double to Point2F
|
||||
// - double to Point3F
|
||||
// - double to MatrixF
|
||||
//
|
||||
// The _SourceReader object is initialized with a list of parameter names that it
|
||||
// tries to match to <param> elements in the source accessor to figure out how to
|
||||
// pull values out of the 1D source array. Note that no type checking of any kind
|
||||
// is done until we actually try to extract values from the source.
|
||||
class _SourceReader
|
||||
{
|
||||
const domSource* source; // the wrapped Collada source
|
||||
const domAccessor* accessor; // shortcut to the source accessor
|
||||
Vector<U32> offsets; // offset of each of the desired values to pull from the source array
|
||||
|
||||
public:
|
||||
_SourceReader() : source(0), accessor(0) {}
|
||||
|
||||
void reset()
|
||||
{
|
||||
source = 0;
|
||||
accessor = 0;
|
||||
offsets.clear();
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// Initialize the _SourceReader object
|
||||
bool initFromSource(const domSource* src, const char* paramNames[] = 0)
|
||||
{
|
||||
source = src;
|
||||
accessor = source->getTechnique_common()->getAccessor();
|
||||
offsets.clear();
|
||||
|
||||
// The source array has groups of values in a 1D stream => need to map the
|
||||
// input param names to source params to determine the offset within the
|
||||
// group for each desired value
|
||||
U32 paramCount = 0;
|
||||
while (paramNames && paramNames[paramCount][0]) {
|
||||
// lookup the index of the source param that matches the input param
|
||||
offsets.push_back(paramCount);
|
||||
for (U32 iParam = 0; iParam < accessor->getParam_array().getCount(); iParam++) {
|
||||
if (accessor->getParam_array()[iParam]->getName() &&
|
||||
dStrEqual(accessor->getParam_array()[iParam]->getName(), paramNames[paramCount])) {
|
||||
offsets.last() = iParam;
|
||||
break;
|
||||
}
|
||||
}
|
||||
paramCount++;
|
||||
}
|
||||
|
||||
// If no input params were specified, just map the source params directly
|
||||
if (!offsets.size()) {
|
||||
for (int iParam = 0; iParam < accessor->getParam_array().getCount(); iParam++)
|
||||
offsets.push_back(iParam);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// Shortcut to the size of the array (should be the number of destination objects)
|
||||
S32 size() const { return accessor ? accessor->getCount() : 0; }
|
||||
|
||||
// Get the number of elements per group in the source
|
||||
S32 stride() const { return accessor ? accessor->getStride() : 0; }
|
||||
|
||||
//------------------------------------------------------
|
||||
// Get a pointer to the start of a group of values (index advances by stride)
|
||||
//template<class T> T getArrayData(int index) const { return 0; }
|
||||
|
||||
const double* getStringArrayData(int index) const
|
||||
{
|
||||
if ((index >= 0) && (index < size())) {
|
||||
if (source->getFloat_array())
|
||||
return &source->getFloat_array()->getValue()[index*stride()];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// Read a single value from the source array
|
||||
//template<class T> T getValue(int index) const { return T; }
|
||||
|
||||
const char* getStringValue(int index) const
|
||||
{
|
||||
if ((index >= 0) && (index < size())) {
|
||||
// could be plain strings or IDREFs
|
||||
if (source->getName_array())
|
||||
return source->getName_array()->getValue()[index*stride()];
|
||||
else if (source->getIDREF_array())
|
||||
return source->getIDREF_array()->getValue()[index*stride()].getID();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
F32 getFloatValue(int index) const
|
||||
{
|
||||
F32 value(0);
|
||||
if (const double* data = getStringArrayData(index))
|
||||
return data[offsets[0]];
|
||||
return value;
|
||||
}
|
||||
|
||||
Point2F getPoint2FValue(int index) const
|
||||
{
|
||||
Point2F value(0, 0);
|
||||
if (const double* data = getStringArrayData(index))
|
||||
value.set(data[offsets[0]], data[offsets[1]]);
|
||||
return value;
|
||||
}
|
||||
|
||||
Point3F getPoint3FValue(int index) const
|
||||
{
|
||||
Point3F value(1, 0, 0);
|
||||
if (const double* data = getStringArrayData(index))
|
||||
value.set(data[offsets[0]], data[offsets[1]], data[offsets[2]]);
|
||||
return value;
|
||||
}
|
||||
|
||||
ColorI getColorIValue(int index) const
|
||||
{
|
||||
ColorI value(255, 255, 255, 255);
|
||||
if (const double* data = getStringArrayData(index))
|
||||
{
|
||||
value.red = data[offsets[0]] * 255.0;
|
||||
value.green = data[offsets[1]] * 255.0;
|
||||
value.blue = data[offsets[2]] * 255.0;
|
||||
if ( stride() == 4 )
|
||||
value.alpha = data[offsets[3]] * 255.0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
MatrixF getMatrixFValue(int index) const
|
||||
{
|
||||
MatrixF value(true);
|
||||
if (const double* data = getStringArrayData(index)) {
|
||||
for (int i = 0; i < 16; i++)
|
||||
value[i] = data[i];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Collada geometric primitives: Use the BasePrimitive class to access the
|
||||
// different primitive types in a nice way.
|
||||
class BasePrimitive
|
||||
{
|
||||
public:
|
||||
/// Return true if the element is a geometric primitive type
|
||||
static bool isPrimitive(const daeElement* element)
|
||||
{
|
||||
switch (element->getElementType()) {
|
||||
case COLLADA_TYPE::TRIANGLES: case COLLADA_TYPE::POLYLIST:
|
||||
case COLLADA_TYPE::POLYGONS: case COLLADA_TYPE::TRIFANS:
|
||||
case COLLADA_TYPE::TRISTRIPS: case COLLADA_TYPE::CAPSULE:
|
||||
case COLLADA_TYPE::CYLINDER: case COLLADA_TYPE::LINES:
|
||||
case COLLADA_TYPE::LINESTRIPS: case COLLADA_TYPE::PLANE:
|
||||
case COLLADA_TYPE::SPLINE: case COLLADA_TYPE::SPHERE:
|
||||
case COLLADA_TYPE::TAPERED_CAPSULE: case COLLADA_TYPE::TAPERED_CYLINDER:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Return true if the element is a supported primitive type
|
||||
static bool isSupportedPrimitive(const daeElement* element)
|
||||
{
|
||||
switch (element->getElementType()) {
|
||||
case COLLADA_TYPE::TRIANGLES:
|
||||
case COLLADA_TYPE::TRISTRIPS:
|
||||
case COLLADA_TYPE::TRIFANS:
|
||||
case COLLADA_TYPE::POLYLIST:
|
||||
case COLLADA_TYPE::POLYGONS:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Construct a child class based on the type of Collada element
|
||||
static BasePrimitive* get(const daeElement* element);
|
||||
|
||||
/// Methods to be implemented for each supported Collada geometric element
|
||||
virtual const char* getElementName() = 0;
|
||||
virtual const char* getMaterial() = 0;
|
||||
virtual const domInputLocalOffset_Array& getInputs() = 0;
|
||||
|
||||
virtual S32 getStride() const = 0;
|
||||
virtual const domListOfUInts *getTriangleData() = 0;
|
||||
};
|
||||
|
||||
/// Template child class for supported Collada primitive elements
|
||||
template<class T> class ColladaPrimitive : public BasePrimitive
|
||||
{
|
||||
T* primitive;
|
||||
domListOfUInts *pTriangleData;
|
||||
S32 stride;
|
||||
public:
|
||||
ColladaPrimitive(const daeElement* e) : pTriangleData(0)
|
||||
{
|
||||
// Cast to geometric primitive element
|
||||
primitive = daeSafeCast<T>(const_cast<daeElement*>(e));
|
||||
|
||||
// Determine stride
|
||||
stride = 0;
|
||||
for (int iInput = 0; iInput < getInputs().getCount(); iInput++) {
|
||||
if (getInputs()[iInput]->getOffset() >= stride)
|
||||
stride = getInputs()[iInput]->getOffset() + 1;
|
||||
}
|
||||
}
|
||||
~ColladaPrimitive()
|
||||
{
|
||||
delete pTriangleData;
|
||||
}
|
||||
|
||||
/// Most primitives can use these common implementations
|
||||
const char* getElementName() { return primitive->getElementName(); }
|
||||
const char* getMaterial() { return primitive->getMaterial(); }
|
||||
const domInputLocalOffset_Array& getInputs() { return primitive->getInput_array(); }
|
||||
S32 getStride() const { return stride; }
|
||||
|
||||
/// Each supported primitive needs to implement this method (and convert
|
||||
/// to triangles if required)
|
||||
const domListOfUInts *getTriangleData() { return NULL; }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// <triangles>
|
||||
template<> inline const domListOfUInts *ColladaPrimitive<domTriangles>::getTriangleData()
|
||||
{
|
||||
// Return the <p> integer list directly
|
||||
return (primitive->getP() ? &(primitive->getP()->getValue()) : NULL);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// <tristrips>
|
||||
template<> inline const domListOfUInts *ColladaPrimitive<domTristrips>::getTriangleData()
|
||||
{
|
||||
if (!pTriangleData)
|
||||
{
|
||||
// Convert strips to triangles
|
||||
pTriangleData = new domListOfUInts();
|
||||
|
||||
for (int iStrip = 0; iStrip < primitive->getCount(); iStrip++) {
|
||||
|
||||
domP* P = primitive->getP_array()[iStrip];
|
||||
|
||||
// Ignore invalid P arrays
|
||||
if (!P || !P->getValue().getCount())
|
||||
continue;
|
||||
|
||||
domUint* pSrcData = &(P->getValue()[0]);
|
||||
S32 numTriangles = (P->getValue().getCount() / stride) - 2;
|
||||
|
||||
// Convert the strip back to a triangle list
|
||||
domUint* v0 = pSrcData;
|
||||
for (int iTri = 0; iTri < numTriangles; iTri++, v0 += stride) {
|
||||
if (iTri & 0x1)
|
||||
{
|
||||
// CW triangle
|
||||
pTriangleData->appendArray(stride, v0);
|
||||
pTriangleData->appendArray(stride, v0 + 2*stride);
|
||||
pTriangleData->appendArray(stride, v0 + stride);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CCW triangle
|
||||
pTriangleData->appendArray(stride*3, v0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return pTriangleData;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// <trifans>
|
||||
template<> inline const domListOfUInts *ColladaPrimitive<domTrifans>::getTriangleData()
|
||||
{
|
||||
if (!pTriangleData)
|
||||
{
|
||||
// Convert strips to triangles
|
||||
pTriangleData = new domListOfUInts();
|
||||
|
||||
for (int iStrip = 0; iStrip < primitive->getCount(); iStrip++) {
|
||||
|
||||
domP* P = primitive->getP_array()[iStrip];
|
||||
|
||||
// Ignore invalid P arrays
|
||||
if (!P || !P->getValue().getCount())
|
||||
continue;
|
||||
|
||||
domUint* pSrcData = &(P->getValue()[0]);
|
||||
S32 numTriangles = (P->getValue().getCount() / stride) - 2;
|
||||
|
||||
// Convert the fan back to a triangle list
|
||||
domUint* v0 = pSrcData + stride;
|
||||
for (int iTri = 0; iTri < numTriangles; iTri++, v0 += stride) {
|
||||
pTriangleData->appendArray(stride, pSrcData); // shared vertex
|
||||
pTriangleData->appendArray(stride, v0); // previous vertex
|
||||
pTriangleData->appendArray(stride, v0+stride); // current vertex
|
||||
}
|
||||
}
|
||||
}
|
||||
return pTriangleData;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// <polygons>
|
||||
template<> inline const domListOfUInts *ColladaPrimitive<domPolygons>::getTriangleData()
|
||||
{
|
||||
if (!pTriangleData)
|
||||
{
|
||||
// Convert polygons to triangles
|
||||
pTriangleData = new domListOfUInts();
|
||||
|
||||
for (int iPoly = 0; iPoly < primitive->getCount(); iPoly++) {
|
||||
|
||||
domP* P = primitive->getP_array()[iPoly];
|
||||
|
||||
// Ignore invalid P arrays
|
||||
if (!P || !P->getValue().getCount())
|
||||
continue;
|
||||
|
||||
domUint* pSrcData = &(P->getValue()[0]);
|
||||
S32 numPoints = P->getValue().getCount() / stride;
|
||||
|
||||
// Use a simple tri-fan (centered at the first point) method of
|
||||
// converting the polygon to triangles.
|
||||
domUint* v0 = pSrcData;
|
||||
pSrcData += stride;
|
||||
for (int iTri = 0; iTri < numPoints-2; iTri++) {
|
||||
pTriangleData->appendArray(stride, v0);
|
||||
pTriangleData->appendArray(stride*2, pSrcData);
|
||||
pSrcData += stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pTriangleData;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// <polylist>
|
||||
template<> inline const domListOfUInts *ColladaPrimitive<domPolylist>::getTriangleData()
|
||||
{
|
||||
if (!pTriangleData)
|
||||
{
|
||||
// Convert polygons to triangles
|
||||
pTriangleData = new domListOfUInts();
|
||||
|
||||
// Check that the P element has the right number of values (this
|
||||
// has been seen with certain models exported using COLLADAMax)
|
||||
const domListOfUInts& vcount = primitive->getVcount()->getValue();
|
||||
|
||||
U32 expectedCount = 0;
|
||||
for (int iPoly = 0; iPoly < vcount.getCount(); iPoly++)
|
||||
expectedCount += vcount[iPoly];
|
||||
expectedCount *= stride;
|
||||
|
||||
if (!primitive->getP() || !primitive->getP()->getValue().getCount() ||
|
||||
(primitive->getP()->getValue().getCount() != expectedCount) )
|
||||
{
|
||||
Con::warnf("<polylist> element found with invalid <p> array. This primitive will be ignored.");
|
||||
return pTriangleData;
|
||||
}
|
||||
|
||||
domUint* pSrcData = &(primitive->getP()->getValue()[0]);
|
||||
for (int iPoly = 0; iPoly < vcount.getCount(); iPoly++) {
|
||||
|
||||
// Use a simple tri-fan (centered at the first point) method of
|
||||
// converting the polygon to triangles.
|
||||
domUint* v0 = pSrcData;
|
||||
pSrcData += stride;
|
||||
for (int iTri = 0; iTri < vcount[iPoly]-2; iTri++) {
|
||||
pTriangleData->appendArray(stride, v0);
|
||||
pTriangleData->appendArray(stride*2, pSrcData);
|
||||
pSrcData += stride;
|
||||
}
|
||||
pSrcData += stride;
|
||||
}
|
||||
}
|
||||
return pTriangleData;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// Convert a custom parameter string to a particular type
|
||||
template<typename T> inline T convert(const char* value) { return value; }
|
||||
template<> inline bool convert(const char* value) { return dAtob(value); }
|
||||
template<> inline S32 convert(const char* value) { return dAtoi(value); }
|
||||
template<> inline double convert(const char* value) { return dAtof(value); }
|
||||
template<> inline F32 convert(const char* value) { return convert<double>(value); }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Collada animation data
|
||||
struct AnimChannels : public Vector<struct AnimData*>
|
||||
{
|
||||
daeElement *element;
|
||||
AnimChannels(daeElement* el) : element(el)
|
||||
{
|
||||
element->setUserData(this);
|
||||
}
|
||||
~AnimChannels()
|
||||
{
|
||||
if (element)
|
||||
element->setUserData(0);
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimData
|
||||
{
|
||||
bool enabled; ///!< Used to select animation channels for the current clip
|
||||
|
||||
_SourceReader input;
|
||||
_SourceReader output;
|
||||
|
||||
_SourceReader inTangent;
|
||||
_SourceReader outTangent;
|
||||
|
||||
_SourceReader interpolation;
|
||||
|
||||
U32 targetValueOffset; ///< Offset into the target element (for arrays of values)
|
||||
U32 targetValueCount; ///< Number of values animated (from OUTPUT source array)
|
||||
|
||||
/// Get the animation channels for the Collada element (if any)
|
||||
static AnimChannels* getAnimChannels(const daeElement* element)
|
||||
{
|
||||
return element ? (AnimChannels*)const_cast<daeElement*>(element)->getUserData() : 0;
|
||||
}
|
||||
|
||||
AnimData() : enabled(false) { }
|
||||
|
||||
void parseTargetString(const char* target, int fullCount, const char* elements[]);
|
||||
|
||||
F32 invertParamCubic(F32 param, F32 x0, F32 x1, F32 x2, F32 x3) const;
|
||||
void interpValue(F32 t, U32 offset, double* value) const;
|
||||
void interpValue(F32 t, U32 offset, const char** value) const;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Collada allows any element with an SID or ID attribute to be the target of
|
||||
// an animation channel, which is very flexible, but awkward to work with. Some
|
||||
// examples of animated values are:
|
||||
// - single float
|
||||
// - single int
|
||||
// - single bool
|
||||
// - single string
|
||||
// - list of floats (transform elements or morph weights)
|
||||
//
|
||||
// This class provides a generic way to check if an element is animated, and
|
||||
// to get the value of the element at a given time.
|
||||
template<class T>
|
||||
struct AnimatedElement
|
||||
{
|
||||
const daeElement* element; ///< The Collada element (can be NULL)
|
||||
T defaultVal; ///< Default value (used when element is NULL)
|
||||
|
||||
AnimatedElement(const daeElement* e=0) : element(e) { }
|
||||
|
||||
/// Check if the element has any animations channels
|
||||
bool isAnimated() { return (AnimData::getAnimChannels(element) != 0); }
|
||||
bool isAnimated(F32 start, F32 end) { return isAnimated(); }
|
||||
|
||||
/// Get the value of the element at the specified time
|
||||
T getValue(F32 time)
|
||||
{
|
||||
// If the element is NULL, just use the default (handy for <extra> profiles which
|
||||
// may or may not be present in the document)
|
||||
T value(defaultVal);
|
||||
if (const domAny* param = daeSafeCast<domAny>(const_cast<daeElement*>(element))) {
|
||||
// If the element is not animated, just use its current value
|
||||
value = convert<T>(param->getValue());
|
||||
|
||||
// Animate the value
|
||||
const AnimChannels* channels = AnimData::getAnimChannels(element);
|
||||
if (channels && (time >= 0)) {
|
||||
for (int iChannel = 0; iChannel < channels->size(); iChannel++) {
|
||||
const AnimData* animData = (*channels)[iChannel];
|
||||
if (animData->enabled)
|
||||
animData->interpValue(time, 0, &value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T> struct AnimatedElementList : public AnimatedElement<T>
|
||||
{
|
||||
AnimatedElementList(const daeElement* e=0) : AnimatedElement<T>(e) { }
|
||||
|
||||
// @todo: Disable morph animations for now since they are not supported by T3D
|
||||
bool isAnimated() { return false; }
|
||||
bool isAnimated(F32 start, F32 end) { return false; }
|
||||
|
||||
// Get the value of the element list at the specified time
|
||||
T getValue(F32 time)
|
||||
{
|
||||
T vec(this->defaultVal);
|
||||
if (this->element) {
|
||||
// Get a copy of the vector
|
||||
vec = *(T*)const_cast<daeElement*>(this->element)->getValuePointer();
|
||||
|
||||
// Animate the vector
|
||||
const AnimChannels* channels = AnimData::getAnimChannels(this->element);
|
||||
if (channels && (time >= 0)) {
|
||||
for (int iChannel = 0; iChannel < channels->size(); iChannel++) {
|
||||
const AnimData* animData = (*channels)[iChannel];
|
||||
if (animData->enabled) {
|
||||
for (int iValue = 0; iValue < animData->targetValueCount; iValue++)
|
||||
animData->interpValue(time, iValue, &vec[animData->targetValueOffset + iValue]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
};
|
||||
|
||||
// Strongly typed animated values
|
||||
typedef AnimatedElement<double> AnimatedFloat;
|
||||
typedef AnimatedElement<bool> AnimatedBool;
|
||||
typedef AnimatedElement<S32> AnimatedInt;
|
||||
typedef AnimatedElement<const char*> AnimatedString;
|
||||
typedef AnimatedElementList<domListOfFloats> AnimatedFloatList;
|
||||
|
||||
#endif // _COLLADA_UTILS_H_
|
||||
67
Engine/source/ts/instancingMatHook.cpp
Normal file
67
Engine/source/ts/instancingMatHook.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/instancingMatHook.h"
|
||||
|
||||
#include "platform/profiler.h"
|
||||
#include "materials/materialFeatureTypes.h"
|
||||
#include "materials/matInstance.h"
|
||||
|
||||
|
||||
const MatInstanceHookType InstancingMaterialHook::Type( "Instancing" );
|
||||
|
||||
InstancingMaterialHook::InstancingMaterialHook() :
|
||||
mMatInst( NULL )
|
||||
{
|
||||
}
|
||||
|
||||
InstancingMaterialHook::~InstancingMaterialHook()
|
||||
{
|
||||
SAFE_DELETE( mMatInst );
|
||||
}
|
||||
|
||||
BaseMatInstance* InstancingMaterialHook::getInstancingMat( BaseMatInstance *matInst )
|
||||
{
|
||||
PROFILE_SCOPE( InstancingMaterialHook_GetInstancingMat );
|
||||
|
||||
if ( matInst == NULL )
|
||||
return NULL;
|
||||
|
||||
InstancingMaterialHook *hook = matInst->getHook<InstancingMaterialHook>();
|
||||
if ( hook == NULL )
|
||||
{
|
||||
hook = new InstancingMaterialHook();
|
||||
matInst->addHook( hook );
|
||||
|
||||
BaseMatInstance *instMat = matInst->getMaterial()->createMatInstance();
|
||||
FeatureSet features( matInst->getRequestedFeatures() );
|
||||
features.addFeature( MFT_UseInstancing );
|
||||
|
||||
if ( !instMat->init( features, matInst->getVertexFormat() ) )
|
||||
SAFE_DELETE( instMat );
|
||||
|
||||
hook->mMatInst = instMat;
|
||||
}
|
||||
|
||||
return hook->mMatInst;
|
||||
}
|
||||
57
Engine/source/ts/instancingMatHook.h
Normal file
57
Engine/source/ts/instancingMatHook.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _INSTANCINGMATHOOK_H_
|
||||
#define _INSTANCINGMATHOOK_H_
|
||||
|
||||
#ifndef _MATINSTANCEHOOK_H_
|
||||
#include "materials/matInstanceHook.h"
|
||||
#endif
|
||||
|
||||
class BaseMatInstance;
|
||||
|
||||
|
||||
class InstancingMaterialHook : public MatInstanceHook
|
||||
{
|
||||
public:
|
||||
|
||||
/// The material hook type.
|
||||
static const MatInstanceHookType Type;
|
||||
|
||||
InstancingMaterialHook();
|
||||
virtual ~InstancingMaterialHook();
|
||||
|
||||
// MatInstanceHook
|
||||
virtual const MatInstanceHookType& getType() const { return Type; }
|
||||
|
||||
/// Returns the instancing material instance or the input material
|
||||
/// instance if one could not be created.
|
||||
static BaseMatInstance* getInstancingMat( BaseMatInstance *matInst );
|
||||
|
||||
protected:
|
||||
|
||||
/// The instancing material.
|
||||
BaseMatInstance *mMatInst;
|
||||
|
||||
};
|
||||
|
||||
#endif // _INSTANCINGMATHOOK_H_
|
||||
40
Engine/source/ts/loader/appMaterial.h
Normal file
40
Engine/source/ts/loader/appMaterial.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _APPMATERIAL_H_
|
||||
#define _APPMATERIAL_H_
|
||||
|
||||
|
||||
struct AppMaterial
|
||||
{
|
||||
U32 flags;
|
||||
F32 reflectance;
|
||||
|
||||
AppMaterial() : flags(0), reflectance(1.0f) { }
|
||||
virtual ~AppMaterial() {}
|
||||
|
||||
virtual String getName() const { return "unnamed"; }
|
||||
virtual U32 getFlags() { return flags; }
|
||||
virtual F32 getReflectance() { return reflectance; }
|
||||
};
|
||||
|
||||
#endif // _APPMATERIAL_H_
|
||||
178
Engine/source/ts/loader/appMesh.cpp
Normal file
178
Engine/source/ts/loader/appMesh.cpp
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "ts/loader/appMesh.h"
|
||||
#include "ts/loader/tsShapeLoader.h"
|
||||
|
||||
Vector<AppMaterial*> AppMesh::appMaterials;
|
||||
|
||||
AppMesh::AppMesh()
|
||||
: flags(0), numFrames(0), numMatFrames(0), vertsPerFrame(0)
|
||||
{
|
||||
}
|
||||
|
||||
AppMesh::~AppMesh()
|
||||
{
|
||||
}
|
||||
|
||||
void AppMesh::computeBounds(Box3F& bounds)
|
||||
{
|
||||
bounds = Box3F::Invalid;
|
||||
|
||||
if ( isSkin() )
|
||||
{
|
||||
// Need to skin the mesh before we can compute the bounds
|
||||
|
||||
// Setup bone transforms
|
||||
Vector<MatrixF> boneTransforms;
|
||||
boneTransforms.setSize( nodeIndex.size() );
|
||||
for (S32 iBone = 0; iBone < boneTransforms.size(); iBone++)
|
||||
{
|
||||
MatrixF nodeMat = bones[iBone]->getNodeTransform( TSShapeLoader::DefaultTime );
|
||||
TSShapeLoader::zapScale(nodeMat);
|
||||
boneTransforms[iBone].mul( nodeMat, initialTransforms[iBone] );
|
||||
}
|
||||
|
||||
// Multiply verts by weighted bone transforms
|
||||
for (S32 iVert = 0; iVert < initialVerts.size(); iVert++)
|
||||
points[iVert].set( Point3F::Zero );
|
||||
|
||||
for (S32 iWeight = 0; iWeight < vertexIndex.size(); iWeight++)
|
||||
{
|
||||
const S32& vertIndex = vertexIndex[iWeight];
|
||||
const MatrixF& deltaTransform = boneTransforms[ boneIndex[iWeight] ];
|
||||
|
||||
Point3F v;
|
||||
deltaTransform.mulP( initialVerts[vertIndex], &v );
|
||||
v *= weight[iWeight];
|
||||
|
||||
points[vertIndex] += v;
|
||||
}
|
||||
|
||||
// compute bounds for the skinned mesh
|
||||
for (S32 iVert = 0; iVert < initialVerts.size(); iVert++)
|
||||
bounds.extend( points[iVert] );
|
||||
}
|
||||
else
|
||||
{
|
||||
MatrixF transform = getMeshTransform(TSShapeLoader::DefaultTime);
|
||||
TSShapeLoader::zapScale(transform);
|
||||
|
||||
for (S32 iVert = 0; iVert < points.size(); iVert++)
|
||||
{
|
||||
Point3F p;
|
||||
transform.mulP(points[iVert], &p);
|
||||
bounds.extend(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AppMesh::computeNormals()
|
||||
{
|
||||
// Clear normals
|
||||
normals.setSize( points.size() );
|
||||
for (S32 iNorm = 0; iNorm < normals.size(); iNorm++)
|
||||
normals[iNorm] = Point3F::Zero;
|
||||
|
||||
// Sum triangle normals for each vertex
|
||||
for (S32 iPrim = 0; iPrim < primitives.size(); iPrim++)
|
||||
{
|
||||
const TSDrawPrimitive& prim = primitives[iPrim];
|
||||
|
||||
for (S32 iInd = 0; iInd < prim.numElements; iInd += 3)
|
||||
{
|
||||
// Compute the normal for this triangle
|
||||
S32 idx0 = indices[prim.start + iInd + 0];
|
||||
S32 idx1 = indices[prim.start + iInd + 1];
|
||||
S32 idx2 = indices[prim.start + iInd + 2];
|
||||
|
||||
const Point3F& v0 = points[idx0];
|
||||
const Point3F& v1 = points[idx1];
|
||||
const Point3F& v2 = points[idx2];
|
||||
|
||||
Point3F n;
|
||||
mCross(v2 - v0, v1 - v0, &n);
|
||||
n.normalize(); // remove this to use 'weighted' normals (large triangles will have more effect)
|
||||
|
||||
normals[idx0] += n;
|
||||
normals[idx1] += n;
|
||||
normals[idx2] += n;
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize the vertex normals (this takes care of averaging the triangle normals)
|
||||
for (S32 iNorm = 0; iNorm < normals.size(); iNorm++)
|
||||
normals[iNorm].normalize();
|
||||
}
|
||||
|
||||
TSMesh* AppMesh::constructTSMesh()
|
||||
{
|
||||
TSMesh* tsmesh;
|
||||
if (isSkin())
|
||||
{
|
||||
TSSkinMesh* tsskin = new TSSkinMesh();
|
||||
tsmesh = tsskin;
|
||||
|
||||
// Copy skin elements
|
||||
tsskin->weight = weight;
|
||||
tsskin->boneIndex = boneIndex;
|
||||
tsskin->vertexIndex = vertexIndex;
|
||||
tsskin->batchData.nodeIndex = nodeIndex;
|
||||
tsskin->batchData.initialTransforms = initialTransforms;
|
||||
tsskin->batchData.initialVerts = initialVerts;
|
||||
tsskin->batchData.initialNorms = initialNorms;
|
||||
}
|
||||
else
|
||||
{
|
||||
tsmesh = new TSMesh();
|
||||
}
|
||||
|
||||
// Copy mesh elements
|
||||
tsmesh->verts = points;
|
||||
tsmesh->norms = normals;
|
||||
tsmesh->tverts = uvs;
|
||||
tsmesh->primitives = primitives;
|
||||
tsmesh->indices = indices;
|
||||
tsmesh->colors = colors;
|
||||
tsmesh->tverts2 = uv2s;
|
||||
|
||||
// Finish initializing the shape
|
||||
tsmesh->setFlags(flags);
|
||||
tsmesh->computeBounds();
|
||||
tsmesh->numFrames = numFrames;
|
||||
tsmesh->numMatFrames = numMatFrames;
|
||||
tsmesh->vertsPerFrame = vertsPerFrame;
|
||||
tsmesh->createTangents(tsmesh->verts, tsmesh->norms);
|
||||
tsmesh->encodedNorms.set(NULL,0);
|
||||
|
||||
return tsmesh;
|
||||
}
|
||||
|
||||
bool AppMesh::isBillboard()
|
||||
{
|
||||
return !dStrnicmp(getName(),"BB::",4) || !dStrnicmp(getName(),"BB_",3) || isBillboardZAxis();
|
||||
}
|
||||
|
||||
bool AppMesh::isBillboardZAxis()
|
||||
{
|
||||
return !dStrnicmp(getName(),"BBZ::",5) || !dStrnicmp(getName(),"BBZ_",4);
|
||||
}
|
||||
105
Engine/source/ts/loader/appMesh.h
Normal file
105
Engine/source/ts/loader/appMesh.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _APPMESH_H_
|
||||
#define _APPMESH_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef _APPMATERIAL_H_
|
||||
#include "ts/loader/appMaterial.h"
|
||||
#endif
|
||||
#ifndef _APPSEQUENCE_H_
|
||||
#include "ts/loader/appSequence.h"
|
||||
#endif
|
||||
|
||||
class AppNode;
|
||||
|
||||
class AppMesh
|
||||
{
|
||||
public:
|
||||
// Mesh and skin elements
|
||||
Vector<Point3F> points;
|
||||
Vector<Point3F> normals;
|
||||
Vector<Point2F> uvs;
|
||||
Vector<Point2F> uv2s;
|
||||
Vector<ColorI> colors;
|
||||
Vector<TSDrawPrimitive> primitives;
|
||||
Vector<U32> indices;
|
||||
|
||||
// Skin elements
|
||||
Vector<F32> weight;
|
||||
Vector<S32> boneIndex;
|
||||
Vector<S32> vertexIndex;
|
||||
Vector<S32> nodeIndex;
|
||||
Vector<MatrixF> initialTransforms;
|
||||
Vector<Point3F> initialVerts;
|
||||
Vector<Point3F> initialNorms;
|
||||
|
||||
U32 flags;
|
||||
U32 vertsPerFrame;
|
||||
S32 numFrames;
|
||||
S32 numMatFrames;
|
||||
|
||||
// Loader elements (can be discarded after loading)
|
||||
S32 detailSize;
|
||||
MatrixF objectOffset;
|
||||
Vector<AppNode*> bones;
|
||||
static Vector<AppMaterial*> appMaterials;
|
||||
|
||||
public:
|
||||
AppMesh();
|
||||
virtual ~AppMesh();
|
||||
|
||||
void computeBounds(Box3F& bounds);
|
||||
void computeNormals();
|
||||
|
||||
// Create a TSMesh object
|
||||
TSMesh* constructTSMesh();
|
||||
|
||||
virtual const char * getName(bool allowFixed=true) = 0;
|
||||
|
||||
virtual MatrixF getMeshTransform(F32 time) = 0;
|
||||
virtual F32 getVisValue(F32 time) = 0;
|
||||
|
||||
virtual bool getFloat(const char* propName, F32& defaultVal) = 0;
|
||||
virtual bool getInt(const char* propName, S32& defaultVal) = 0;
|
||||
virtual bool getBool(const char* propName, bool& defaultVal) = 0;
|
||||
|
||||
virtual bool animatesVis(const AppSequence* appSeq) { return false; }
|
||||
virtual bool animatesMatFrame(const AppSequence* appSeq) { return false; }
|
||||
virtual bool animatesFrame(const AppSequence* appSeq) { return false; }
|
||||
|
||||
virtual bool isBillboard();
|
||||
virtual bool isBillboardZAxis();
|
||||
|
||||
virtual bool isSkin() { return false; }
|
||||
virtual void lookupSkinData() = 0;
|
||||
|
||||
virtual void lockMesh(F32 t, const MatrixF& objectOffset) { }
|
||||
};
|
||||
|
||||
#endif // _APPMESH_H_
|
||||
104
Engine/source/ts/loader/appNode.cpp
Normal file
104
Engine/source/ts/loader/appNode.cpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "ts/loader/appNode.h"
|
||||
|
||||
AppNode::AppNode()
|
||||
{
|
||||
mName = NULL;
|
||||
mParentName = NULL;
|
||||
}
|
||||
|
||||
AppNode::~AppNode()
|
||||
{
|
||||
dFree( mName );
|
||||
dFree( mParentName );
|
||||
|
||||
// delete children and meshes
|
||||
for (S32 i = 0; i < mChildNodes.size(); i++)
|
||||
delete mChildNodes[i];
|
||||
for (S32 i = 0; i < mMeshes.size(); i++)
|
||||
delete mMeshes[i];
|
||||
}
|
||||
|
||||
S32 AppNode::getNumMesh()
|
||||
{
|
||||
if (mMeshes.size() == 0)
|
||||
buildMeshList();
|
||||
return mMeshes.size();
|
||||
}
|
||||
|
||||
AppMesh* AppNode::getMesh(S32 idx)
|
||||
{
|
||||
return (idx < getNumMesh() && idx >= 0) ? mMeshes[idx] : NULL;
|
||||
}
|
||||
|
||||
S32 AppNode::getNumChildNodes()
|
||||
{
|
||||
if (mChildNodes.size() == 0)
|
||||
buildChildList();
|
||||
return mChildNodes.size();
|
||||
}
|
||||
|
||||
AppNode * AppNode::getChildNode(S32 idx)
|
||||
{
|
||||
return (idx < getNumChildNodes() && idx >= 0) ? mChildNodes[idx] : NULL;
|
||||
}
|
||||
|
||||
bool AppNode::isBillboard()
|
||||
{
|
||||
return !dStrnicmp(getName(),"BB::",4) || !dStrnicmp(getName(),"BB_",3) || isBillboardZAxis();
|
||||
}
|
||||
|
||||
bool AppNode::isBillboardZAxis()
|
||||
{
|
||||
return !dStrnicmp(getName(),"BBZ::",5) || !dStrnicmp(getName(),"BBZ_",4);
|
||||
}
|
||||
|
||||
bool AppNode::isDummy()
|
||||
{
|
||||
// naming convention should work well enough...
|
||||
// ...but can override this method if one wants more
|
||||
return !dStrnicmp(getName(), "dummy", 5);
|
||||
}
|
||||
|
||||
bool AppNode::isBounds()
|
||||
{
|
||||
// naming convention should work well enough...
|
||||
// ...but can override this method if one wants more
|
||||
return !dStricmp(getName(), "bounds");
|
||||
}
|
||||
|
||||
bool AppNode::isSequence()
|
||||
{
|
||||
// naming convention should work well enough...
|
||||
// ...but can override this method if one wants more
|
||||
return !dStrnicmp(getName(), "Sequence", 8);
|
||||
}
|
||||
|
||||
bool AppNode::isRoot()
|
||||
{
|
||||
// we assume root node isn't added, so this is never true
|
||||
// but allow for possibility (by overriding this method)
|
||||
// so that isParentRoot still works.
|
||||
return false;
|
||||
}
|
||||
88
Engine/source/ts/loader/appNode.h
Normal file
88
Engine/source/ts/loader/appNode.h
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _APPNODE_H_
|
||||
#define _APPNODE_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef _APPMESH_H_
|
||||
#include "ts/loader/appMesh.h"
|
||||
#endif
|
||||
|
||||
class AppNode
|
||||
{
|
||||
friend class TSShapeLoader;
|
||||
|
||||
// add attached meshes and child nodes to app node
|
||||
// the reason these are tracked by AppNode is that
|
||||
// AppNode is responsible for deleting all it's children
|
||||
// and attached meshes.
|
||||
virtual void buildMeshList() = 0;
|
||||
virtual void buildChildList() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
S32 mParentIndex;
|
||||
Vector<AppMesh*> mMeshes;
|
||||
Vector<AppNode*> mChildNodes;
|
||||
char* mName;
|
||||
char* mParentName;
|
||||
|
||||
public:
|
||||
|
||||
AppNode();
|
||||
virtual ~AppNode();
|
||||
|
||||
S32 getNumMesh();
|
||||
AppMesh* getMesh(S32 idx);
|
||||
|
||||
S32 getNumChildNodes();
|
||||
AppNode* getChildNode(S32 idx);
|
||||
|
||||
virtual MatrixF getNodeTransform(F32 time) = 0;
|
||||
|
||||
virtual bool isEqual(AppNode* node) = 0;
|
||||
|
||||
virtual bool animatesTransform(const AppSequence* appSeq) = 0;
|
||||
|
||||
virtual const char* getName() = 0;
|
||||
virtual const char* getParentName() = 0;
|
||||
|
||||
virtual bool getFloat(const char* propName, F32& defaultVal) = 0;
|
||||
virtual bool getInt(const char* propName, S32& defaultVal) = 0;
|
||||
virtual bool getBool(const char* propName, bool& defaultVal) = 0;
|
||||
|
||||
virtual bool isBillboard();
|
||||
virtual bool isBillboardZAxis();
|
||||
virtual bool isParentRoot() = 0;
|
||||
virtual bool isDummy();
|
||||
virtual bool isBounds();
|
||||
virtual bool isSequence();
|
||||
virtual bool isRoot();
|
||||
};
|
||||
|
||||
#endif // _APPNODE_H_
|
||||
60
Engine/source/ts/loader/appSequence.h
Normal file
60
Engine/source/ts/loader/appSequence.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _APPSEQUENCE_H_
|
||||
#define _APPSEQUENCE_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPE_H_
|
||||
#include "ts/tsShape.h"
|
||||
#endif
|
||||
|
||||
class AppSequence
|
||||
{
|
||||
public:
|
||||
S32 fps;
|
||||
|
||||
public:
|
||||
AppSequence() { }
|
||||
virtual ~AppSequence() { }
|
||||
|
||||
virtual void setActive(bool active) { }
|
||||
|
||||
virtual S32 getNumTriggers() const { return 0; }
|
||||
virtual void getTrigger(S32 index, TSShape::Trigger& trigger) const { trigger.state = 0;}
|
||||
|
||||
virtual const char* getName() const { return "ambient"; }
|
||||
|
||||
virtual F32 getStart() const { return 0.0f; }
|
||||
virtual F32 getEnd() const { return 0.0f; }
|
||||
|
||||
virtual U32 getFlags() const { return 0; }
|
||||
virtual F32 getPriority() const { return 5; }
|
||||
virtual F32 getBlendRefTime() const { return 0.0f; }
|
||||
};
|
||||
|
||||
#endif // _APPSEQUENCE_H_
|
||||
1272
Engine/source/ts/loader/tsShapeLoader.cpp
Normal file
1272
Engine/source/ts/loader/tsShapeLoader.cpp
Normal file
File diff suppressed because it is too large
Load diff
183
Engine/source/ts/loader/tsShapeLoader.h
Normal file
183
Engine/source/ts/loader/tsShapeLoader.h
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSSHAPE_LOADER_H_
|
||||
#define _TSSHAPE_LOADER_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPE_H_
|
||||
#include "ts/tsShape.h"
|
||||
#endif
|
||||
#ifndef _APPNODE_H_
|
||||
#include "ts/loader/appNode.h"
|
||||
#endif
|
||||
#ifndef _APPMESH_H_
|
||||
#include "ts/loader/appMesh.h"
|
||||
#endif
|
||||
#ifndef _APPSEQUENCE_H_
|
||||
#include "ts/loader/appSequence.h"
|
||||
#endif
|
||||
|
||||
class TSShapeLoader
|
||||
{
|
||||
|
||||
public:
|
||||
enum eLoadPhases
|
||||
{
|
||||
Load_ReadFile = 0,
|
||||
Load_ParseFile,
|
||||
Load_ExternalRefs,
|
||||
Load_EnumerateScene,
|
||||
Load_GenerateSubshapes,
|
||||
Load_GenerateObjects,
|
||||
Load_GenerateDefaultStates,
|
||||
Load_GenerateSkins,
|
||||
Load_GenerateMaterials,
|
||||
Load_GenerateSequences,
|
||||
Load_InitShape,
|
||||
NumLoadPhases,
|
||||
Load_Complete = NumLoadPhases
|
||||
};
|
||||
|
||||
static void updateProgress(int major, const char* msg, int numMinor=0, int minor=0);
|
||||
|
||||
protected:
|
||||
struct Subshape
|
||||
{
|
||||
Vector<AppNode*> branches; ///< Shape branches
|
||||
Vector<AppMesh*> objMeshes; ///< Object meshes for this subshape
|
||||
Vector<S32> objNodes; ///< AppNode indices with objects attached
|
||||
|
||||
~Subshape()
|
||||
{
|
||||
// Delete children
|
||||
for (S32 i = 0; i < branches.size(); i++)
|
||||
delete branches[i];
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
static const F32 DefaultTime;
|
||||
static const double MinFrameRate;
|
||||
static const double MaxFrameRate;
|
||||
static const double AppGroundFrameRate;
|
||||
|
||||
protected:
|
||||
// Variables used during loading that must be held until the shape is deleted
|
||||
TSShape* shape;
|
||||
Vector<AppMesh*> appMeshes;
|
||||
|
||||
// Variables used during loading, but that can be discarded afterwards
|
||||
static Torque::Path shapePath;
|
||||
|
||||
AppNode* boundsNode;
|
||||
Vector<AppNode*> appNodes; ///< Nodes in the loaded shape
|
||||
Vector<AppSequence*> appSequences;
|
||||
|
||||
Vector<Subshape*> subshapes;
|
||||
|
||||
Vector<QuatF*> nodeRotCache;
|
||||
Vector<Point3F*> nodeTransCache;
|
||||
Vector<QuatF*> nodeScaleRotCache;
|
||||
Vector<Point3F*> nodeScaleCache;
|
||||
|
||||
Point3F shapeOffset; ///< Offset used to translate the shape origin
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Collect the nodes, objects and sequences for the scene
|
||||
virtual void enumerateScene() = 0;
|
||||
bool processNode(AppNode* node);
|
||||
virtual bool ignoreNode(const String& name) { return false; }
|
||||
virtual bool ignoreMesh(const String& name) { return false; }
|
||||
|
||||
void addSkin(AppMesh* mesh);
|
||||
void addDetailMesh(AppMesh* mesh);
|
||||
void addSubshape(AppNode* node);
|
||||
void addObject(AppMesh* mesh, S32 nodeIndex, S32 subShapeNum);
|
||||
|
||||
// Node transform methods
|
||||
MatrixF getLocalNodeMatrix(AppNode* node, F32 t);
|
||||
void generateNodeTransform(AppNode* node, F32 t, bool blend, F32 referenceTime,
|
||||
QuatF& rot, Point3F& trans, QuatF& srot, Point3F& scale);
|
||||
|
||||
virtual void computeBounds(Box3F& bounds);
|
||||
|
||||
// Create objects, materials and sequences
|
||||
void recurseSubshape(AppNode* appNode, S32 parentIndex, bool recurseChildren);
|
||||
|
||||
void generateSubshapes();
|
||||
void generateObjects();
|
||||
void generateSkins();
|
||||
void generateDefaultStates();
|
||||
void generateObjectState(TSShape::Object& obj, F32 t, bool addFrame, bool addMatFrame);
|
||||
void generateFrame(TSShape::Object& obj, F32 t, bool addFrame, bool addMatFrame);
|
||||
|
||||
void generateMaterialList();
|
||||
|
||||
void generateSequences();
|
||||
|
||||
// Determine what is actually animated in the sequence
|
||||
void setNodeMembership(TSShape::Sequence& seq, const AppSequence* appSeq);
|
||||
void setRotationMembership(TSShape::Sequence& seq);
|
||||
void setTranslationMembership(TSShape::Sequence& seq);
|
||||
void setScaleMembership(TSShape::Sequence& seq);
|
||||
void setObjectMembership(TSShape::Sequence& seq, const AppSequence* appSeq);
|
||||
|
||||
// Manage a cache of all node transform elements for the sequence
|
||||
void clearNodeTransformCache();
|
||||
void fillNodeTransformCache(TSShape::Sequence& seq, const AppSequence* appSeq);
|
||||
|
||||
// Add node transform elements
|
||||
void addNodeRotation(QuatF& rot, bool defaultVal);
|
||||
void addNodeTranslation(Point3F& trans, bool defaultVal);
|
||||
void addNodeUniformScale(F32 scale);
|
||||
void addNodeAlignedScale(Point3F& scale);
|
||||
void addNodeArbitraryScale(QuatF& qrot, Point3F& scale);
|
||||
|
||||
// Generate animation data
|
||||
void generateNodeAnimation(TSShape::Sequence& seq);
|
||||
void generateObjectAnimation(TSShape::Sequence& seq, const AppSequence* appSeq);
|
||||
void generateGroundAnimation(TSShape::Sequence& seq, const AppSequence* appSeq);
|
||||
void generateFrameTriggers(TSShape::Sequence& seq, const AppSequence* appSeq);
|
||||
|
||||
// Shape construction
|
||||
void sortDetails();
|
||||
void install();
|
||||
|
||||
public:
|
||||
TSShapeLoader() : boundsNode(0) { }
|
||||
virtual ~TSShapeLoader();
|
||||
|
||||
static const Torque::Path& getShapePath() { return shapePath; }
|
||||
|
||||
static void zapScale(MatrixF& mat);
|
||||
|
||||
TSShape* generateShape(const Torque::Path& path);
|
||||
};
|
||||
|
||||
#endif // _TSSHAPE_LOADER_H_
|
||||
1123
Engine/source/ts/tsAnimate.cpp
Normal file
1123
Engine/source/ts/tsAnimate.cpp
Normal file
File diff suppressed because it is too large
Load diff
1702
Engine/source/ts/tsCollision.cpp
Normal file
1702
Engine/source/ts/tsCollision.cpp
Normal file
File diff suppressed because it is too large
Load diff
107
Engine/source/ts/tsDecal.cpp
Normal file
107
Engine/source/ts/tsDecal.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "ts/tsDecal.h"
|
||||
#include "math/mMath.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "core/frameAllocator.h"
|
||||
|
||||
//-----------------------------------------------------
|
||||
// TSDecalMesh assembly/dissembly methods
|
||||
// used for transfer to/from memory buffers
|
||||
//-----------------------------------------------------
|
||||
|
||||
#define tsalloc TSShape::smTSAlloc
|
||||
|
||||
void TSDecalMesh::assemble(bool)
|
||||
{
|
||||
if (TSShape::smReadVersion<20)
|
||||
{
|
||||
// read empty mesh...decals used to be derived from meshes
|
||||
tsalloc.checkGuard();
|
||||
tsalloc.getPointer32(15);
|
||||
}
|
||||
|
||||
S32 sz = tsalloc.get32();
|
||||
S32 * ptr32 = tsalloc.copyToShape32(0); // get current shape address w/o doing anything
|
||||
for (S32 i=0; i<sz; i++)
|
||||
{
|
||||
tsalloc.getPointer16(2);
|
||||
tsalloc.getPointer32(1);
|
||||
}
|
||||
tsalloc.align32();
|
||||
primitives.set(ptr32,sz);
|
||||
|
||||
sz = tsalloc.get32();
|
||||
S16 * ptr16 = tsalloc.getPointer16(sz);
|
||||
tsalloc.align32();
|
||||
indices.set(ptr16,sz);
|
||||
|
||||
if (TSShape::smReadVersion<20)
|
||||
{
|
||||
// read more empty mesh stuff...decals used to be derived from meshes
|
||||
tsalloc.getPointer32(3);
|
||||
tsalloc.checkGuard();
|
||||
}
|
||||
|
||||
sz = tsalloc.get32();
|
||||
ptr32 = tsalloc.getPointer32(sz);
|
||||
startPrimitive.set(ptr32,sz);
|
||||
|
||||
if (TSShape::smReadVersion>=19)
|
||||
{
|
||||
ptr32 = tsalloc.getPointer32(sz*4);
|
||||
texgenS.set(ptr32,startPrimitive.size());
|
||||
ptr32 = tsalloc.getPointer32(sz*4);
|
||||
texgenT.set(ptr32,startPrimitive.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
texgenS.set(NULL,0);
|
||||
texgenT.set(NULL,0);
|
||||
}
|
||||
|
||||
materialIndex = tsalloc.get32();
|
||||
|
||||
tsalloc.checkGuard();
|
||||
}
|
||||
|
||||
void TSDecalMesh::disassemble()
|
||||
{
|
||||
tsalloc.set32(primitives.size());
|
||||
tsalloc.copyToBuffer32((S32*)primitives.address(),primitives.size());
|
||||
|
||||
tsalloc.set32(indices.size());
|
||||
tsalloc.copyToBuffer32((S32*)indices.address(),indices.size());
|
||||
|
||||
tsalloc.set32(startPrimitive.size());
|
||||
tsalloc.copyToBuffer32((S32*)startPrimitive.address(),startPrimitive.size());
|
||||
|
||||
tsalloc.copyToBuffer32((S32*)texgenS.address(),texgenS.size()*4);
|
||||
tsalloc.copyToBuffer32((S32*)texgenT.address(),texgenT.size()*4);
|
||||
|
||||
tsalloc.set32(materialIndex);
|
||||
|
||||
tsalloc.setGuard();
|
||||
}
|
||||
|
||||
66
Engine/source/ts/tsDecal.h
Normal file
66
Engine/source/ts/tsDecal.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSDECAL_H_
|
||||
#define _TSDECAL_H_
|
||||
|
||||
#ifndef _TSMESH_H_
|
||||
#include "ts/tsMesh.h"
|
||||
#endif
|
||||
|
||||
/// Decals! The lovely detailing thingies, e.g. bullet hole marks.
|
||||
/// DEPRECATED: This class is here for compatibility with old files only.
|
||||
/// Performs no actual rendering.
|
||||
class TSDecalMesh
|
||||
{
|
||||
public:
|
||||
|
||||
/// The mesh that we are decaling
|
||||
TSMesh * targetMesh;
|
||||
|
||||
/// @name Topology
|
||||
/// @{
|
||||
Vector<TSDrawPrimitive> primitives;
|
||||
Vector<U16> indices;
|
||||
/// @}
|
||||
|
||||
/// @name Render Data
|
||||
/// indexed by decal frame...
|
||||
/// @{
|
||||
Vector<S32> startPrimitive;
|
||||
Vector<Point4F> texgenS;
|
||||
Vector<Point4F> texgenT;
|
||||
/// @}
|
||||
|
||||
/// We only allow 1 material per decal...
|
||||
S32 materialIndex;
|
||||
|
||||
/// DEPRECATED
|
||||
// void render(S32 frame, S32 decalFrame, TSMaterialList *);
|
||||
|
||||
void disassemble();
|
||||
void assemble(bool skip);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
231
Engine/source/ts/tsDump.cpp
Normal file
231
Engine/source/ts/tsDump.cpp
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/tsShapeInstance.h"
|
||||
#include "ts/tsMaterialList.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Dump shape structure:
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
#define dumpLine(buffer) {str = buffer; stream.write((int)dStrlen(str),str);}
|
||||
|
||||
void TSShapeInstance::dumpNode(Stream & stream ,S32 level, S32 nodeIndex, Vector<S32> & detailSizes)
|
||||
{
|
||||
if (nodeIndex < 0)
|
||||
return;
|
||||
|
||||
// limit level to prevent overflow
|
||||
if (level > 160)
|
||||
level = 160;
|
||||
|
||||
S32 i;
|
||||
const char * str;
|
||||
char space[512];
|
||||
for (i = 0; i < level*3; i++)
|
||||
space[i] = ' ';
|
||||
space[level*3] = '\0';
|
||||
|
||||
const char *nodeName = "";
|
||||
const TSShape::Node & node = mShape->nodes[nodeIndex];
|
||||
if (node.nameIndex != -1)
|
||||
nodeName = mShape->getName(node.nameIndex);
|
||||
dumpLine(avar("%s%s", space, nodeName));
|
||||
|
||||
// find all the objects that hang off this node...
|
||||
Vector<ObjectInstance*> objectList;
|
||||
for (i=0; i<mMeshObjects.size(); i++)
|
||||
if (mMeshObjects[i].nodeIndex == nodeIndex)
|
||||
objectList.push_back(&mMeshObjects[i]);
|
||||
|
||||
if (objectList.size() == 0)
|
||||
dumpLine("\r\n");
|
||||
|
||||
S32 spaceCount = -1;
|
||||
for (S32 j=0;j<objectList.size(); j++)
|
||||
{
|
||||
// should be a dynamic cast, but MSVC++ has problems with this...
|
||||
MeshObjectInstance * obj = (MeshObjectInstance *)(objectList[j]);
|
||||
if (!obj)
|
||||
continue;
|
||||
|
||||
// object name
|
||||
const char *objectName = "";
|
||||
if (obj->object->nameIndex!=-1)
|
||||
objectName = mShape->getName(obj->object->nameIndex);
|
||||
|
||||
// more spaces if this is the second object on this node
|
||||
if (spaceCount>0)
|
||||
{
|
||||
char buf[2048];
|
||||
dMemset(buf,' ',spaceCount);
|
||||
buf[spaceCount] = '\0';
|
||||
dumpLine(buf);
|
||||
}
|
||||
|
||||
// dump object name
|
||||
dumpLine(avar(" --> Object %s with following details: ",objectName));
|
||||
|
||||
// dump object detail levels
|
||||
for (S32 k=0; k<obj->object->numMeshes; k++)
|
||||
{
|
||||
S32 f = obj->object->startMeshIndex;
|
||||
if (mShape->meshes[f+k])
|
||||
dumpLine(avar(" %i",detailSizes[k]));
|
||||
}
|
||||
|
||||
dumpLine("\r\n");
|
||||
|
||||
// how many spaces should we prepend if we have another object on this node
|
||||
if (spaceCount<0)
|
||||
spaceCount = (S32)(dStrlen(space) + dStrlen(nodeName));
|
||||
|
||||
if(spaceCount > 2000)
|
||||
spaceCount = 2000;
|
||||
}
|
||||
|
||||
// search for children
|
||||
for (S32 k=nodeIndex+1; k<mShape->nodes.size(); k++)
|
||||
{
|
||||
if (mShape->nodes[k].parentIndex == nodeIndex)
|
||||
// this is our child
|
||||
dumpNode(stream, level+1, k, detailSizes);
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeInstance::dump(Stream & stream)
|
||||
{
|
||||
S32 i,j,ss,od,sz;
|
||||
const char * name;
|
||||
const char * str;
|
||||
|
||||
dumpLine("\r\nShape Hierarchy:\r\n");
|
||||
|
||||
dumpLine("\r\n Details:\r\n");
|
||||
|
||||
for (i=0; i<mShape->details.size(); i++)
|
||||
{
|
||||
const TSDetail & detail = mShape->details[i];
|
||||
name = mShape->getName(detail.nameIndex);
|
||||
ss = detail.subShapeNum;
|
||||
od = detail.objectDetailNum;
|
||||
sz = (S32)detail.size;
|
||||
if (ss >= 0)
|
||||
{
|
||||
dumpLine(avar(" %s, Subtree %i, objectDetail %i, size %i\r\n",name,ss,od,sz));
|
||||
}
|
||||
else
|
||||
{
|
||||
dumpLine(avar(" %s, AutoBillboard, size %i\r\n", name, sz));
|
||||
}
|
||||
}
|
||||
|
||||
dumpLine("\r\n Subtrees:\r\n");
|
||||
|
||||
for (i=0; i<mShape->subShapeFirstNode.size(); i++)
|
||||
{
|
||||
S32 a = mShape->subShapeFirstNode[i];
|
||||
S32 b = a + mShape->subShapeNumNodes[i];
|
||||
dumpLine(avar(" Subtree %i\r\n",i));
|
||||
|
||||
// compute detail sizes for each subshape
|
||||
Vector<S32> detailSizes;
|
||||
for (S32 l=0;l<mShape->details.size(); l++)
|
||||
{
|
||||
if ((mShape->details[l].subShapeNum==i) || (mShape->details[l].subShapeNum==-1))
|
||||
detailSizes.push_back((S32)mShape->details[l].size);
|
||||
}
|
||||
|
||||
for (j=a; j<b; j++)
|
||||
{
|
||||
const TSNode & node = mShape->nodes[j];
|
||||
// if the node has a parent, it'll get dumped via the parent
|
||||
if (node.parentIndex<0)
|
||||
dumpNode(stream,3,j,detailSizes);
|
||||
}
|
||||
}
|
||||
|
||||
bool foundSkin = false;
|
||||
for (i=0; i<mShape->objects.size(); i++)
|
||||
{
|
||||
if (mShape->objects[i].nodeIndex<0) // must be a skin
|
||||
{
|
||||
if (!foundSkin)
|
||||
dumpLine("\r\n Skins:\r\n");
|
||||
foundSkin=true;
|
||||
const char * skinName = "";
|
||||
S32 nameIndex = mShape->objects[i].nameIndex;
|
||||
if (nameIndex>=0)
|
||||
skinName = mShape->getName(nameIndex);
|
||||
dumpLine(avar(" Skin %s with following details: ",skinName));
|
||||
for (S32 num=0; num<mShape->objects[i].numMeshes; num++)
|
||||
{
|
||||
if (mShape->meshes[mShape->objects[i].startMeshIndex + num])
|
||||
dumpLine(avar(" %i",(S32)mShape->details[num].size));
|
||||
}
|
||||
dumpLine("\r\n");
|
||||
}
|
||||
}
|
||||
if (foundSkin)
|
||||
dumpLine("\r\n");
|
||||
|
||||
dumpLine("\r\n Sequences:\r\n");
|
||||
for (i = 0; i < mShape->sequences.size(); i++)
|
||||
{
|
||||
const char *name = "(none)";
|
||||
if (mShape->sequences[i].nameIndex != -1)
|
||||
name = mShape->getName(mShape->sequences[i].nameIndex);
|
||||
dumpLine(avar(" %3d: %s%s%s\r\n", i, name,
|
||||
mShape->sequences[i].isCyclic() ? " (cyclic)" : "",
|
||||
mShape->sequences[i].isBlend() ? " (blend)" : ""));
|
||||
}
|
||||
|
||||
if (mShape->materialList)
|
||||
{
|
||||
TSMaterialList * ml = mShape->materialList;
|
||||
dumpLine("\r\n Material list:\r\n");
|
||||
for (i=0; i<(S32)ml->size(); i++)
|
||||
{
|
||||
U32 flags = ml->getFlags(i);
|
||||
const String& name = ml->getMaterialName(i);
|
||||
dumpLine(avar(
|
||||
" material #%i: '%s'%s.", i, name.c_str(),
|
||||
flags & (TSMaterialList::S_Wrap|TSMaterialList::T_Wrap) ? "" : " not tiled")
|
||||
);
|
||||
if (flags & TSMaterialList::Translucent)
|
||||
{
|
||||
if (flags & TSMaterialList::Additive)
|
||||
dumpLine(" Additive-translucent.")
|
||||
else if (flags & TSMaterialList::Subtractive)
|
||||
dumpLine(" Subtractive-translucent.")
|
||||
else
|
||||
dumpLine(" Translucent.")
|
||||
}
|
||||
dumpLine("\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
306
Engine/source/ts/tsIntegerSet.cpp
Normal file
306
Engine/source/ts/tsIntegerSet.cpp
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "ts/tsIntegerSet.h"
|
||||
#include "platform/platform.h"
|
||||
#include "core/stream/stream.h"
|
||||
|
||||
#define SETUPTO(upto) ( ((1<<(upto&31))-1)*2+1 ) // careful not to shift more than 31 times
|
||||
|
||||
void TSIntegerSet::clearAll(S32 upto)
|
||||
{
|
||||
AssertFatal(upto<=MAX_TS_SET_SIZE,"TSIntegerSet::clearAll: out of range");
|
||||
|
||||
dMemset(bits,0,(upto>>5)*4);
|
||||
if (upto&31)
|
||||
bits[upto>>5] &= ~SETUPTO(upto);
|
||||
}
|
||||
|
||||
void TSIntegerSet::setAll(S32 upto)
|
||||
{
|
||||
AssertFatal(upto<=MAX_TS_SET_SIZE,"TSIntegerSet::setAll: out of range");
|
||||
|
||||
dMemset(bits,0xFFFFFFFF,(upto>>5)*4);
|
||||
if (upto&31)
|
||||
bits[upto>>5] |= SETUPTO(upto);
|
||||
}
|
||||
|
||||
bool TSIntegerSet::testAll(S32 upto) const
|
||||
{
|
||||
AssertFatal(upto<=MAX_TS_SET_SIZE,"TSIntegerSet::testAll: out of range");
|
||||
S32 i;
|
||||
for (i=0; i<(upto>>5); i++)
|
||||
if (bits[i])
|
||||
return true;
|
||||
if (upto&31)
|
||||
return (bits[upto>>5] & SETUPTO(upto)) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
S32 TSIntegerSet::count(S32 upto) const
|
||||
{
|
||||
AssertFatal(upto<=MAX_TS_SET_SIZE,"TSIntegerSet::count: out of range");
|
||||
|
||||
S32 count = 0;
|
||||
U32 dword = bits[0];
|
||||
for (S32 i = 0; i < upto; i++)
|
||||
{
|
||||
// get next word
|
||||
if (!(i & 0x1F))
|
||||
dword = bits[i>>5];
|
||||
// test bits in word
|
||||
if (dword & (1 << (i & 0x1F)))
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void TSIntegerSet::intersect(const TSIntegerSet & otherSet)
|
||||
{
|
||||
for (S32 i=0; i<MAX_TS_SET_DWORDS; i++)
|
||||
bits[i] &= otherSet.bits[i];
|
||||
}
|
||||
|
||||
void TSIntegerSet::overlap(const TSIntegerSet & otherSet)
|
||||
{
|
||||
for (S32 i=0; i<MAX_TS_SET_DWORDS; i++)
|
||||
bits[i] |= otherSet.bits[i];
|
||||
}
|
||||
|
||||
void TSIntegerSet::difference(const TSIntegerSet & otherSet)
|
||||
{
|
||||
for (S32 i=0; i<MAX_TS_SET_DWORDS; i++)
|
||||
bits[i] = (bits[i] | otherSet.bits[i]) & ~(bits[i] & otherSet.bits[i]);
|
||||
}
|
||||
|
||||
void TSIntegerSet::takeAway(const TSIntegerSet & otherSet)
|
||||
{
|
||||
for (S32 i=0; i<MAX_TS_SET_DWORDS; i++)
|
||||
bits[i] &= ~otherSet.bits[i];
|
||||
}
|
||||
|
||||
S32 TSIntegerSet::start() const
|
||||
{
|
||||
for (S32 i=0; i<MAX_TS_SET_DWORDS; i++)
|
||||
{
|
||||
// search for set bit one dword at a time
|
||||
U32 dword = bits[i];
|
||||
if (dword!=0)
|
||||
{
|
||||
// got dword, now search one byte at a time
|
||||
S32 j = 0;
|
||||
U32 mask = 0xFF;
|
||||
do
|
||||
{
|
||||
if (dword&mask)
|
||||
{
|
||||
// got byte, now search one bit at a time
|
||||
U32 bit = mask & ~(mask<<1); // grabs the smallest bit
|
||||
do
|
||||
{
|
||||
if (dword&bit)
|
||||
return (i<<5)+j;
|
||||
j++;
|
||||
bit <<= 1;
|
||||
} while (1);
|
||||
}
|
||||
mask <<= 8;
|
||||
j += 8;
|
||||
} while (1);
|
||||
}
|
||||
}
|
||||
|
||||
return MAX_TS_SET_SIZE;
|
||||
}
|
||||
|
||||
S32 TSIntegerSet::end() const
|
||||
{
|
||||
for (S32 i=MAX_TS_SET_DWORDS-1; i>=0; i--)
|
||||
{
|
||||
// search for set bit one dword at a time
|
||||
U32 dword = bits[i];
|
||||
if (bits[i])
|
||||
{
|
||||
// got dword, now search one byte at a time
|
||||
S32 j = 31;
|
||||
U32 mask = 0xFF000000;
|
||||
do
|
||||
{
|
||||
if (dword&mask)
|
||||
{
|
||||
// got byte, now one bit at a time
|
||||
U32 bit = mask & ~(mask>>1); // grabs the highest bit
|
||||
do
|
||||
{
|
||||
if (dword&bit)
|
||||
return (i<<5)+j+1;
|
||||
j--;
|
||||
bit >>= 1;
|
||||
} while (1);
|
||||
}
|
||||
mask >>= 8;
|
||||
j -= 8;
|
||||
} while (1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TSIntegerSet::next(S32 & i) const
|
||||
{
|
||||
i++;
|
||||
U32 idx = i>>5;
|
||||
U32 bit = 1 << (i&31);
|
||||
U32 dword = bits[idx] & ~(bit-1);
|
||||
while (dword==0)
|
||||
{
|
||||
i = (i+32) & ~31;
|
||||
if (i>=MAX_TS_SET_SIZE)
|
||||
return;
|
||||
dword=bits[++idx];
|
||||
bit = 1;
|
||||
}
|
||||
dword = bits[idx];
|
||||
while ( (bit & dword) == 0)
|
||||
{
|
||||
bit <<= 1;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Or would one byte at a time be better...
|
||||
void TSIntegerSet::next(S32 & i)
|
||||
{
|
||||
U32 idx = i>>3;
|
||||
U8 bit = 1 << (i&7);
|
||||
U8 byte = ((U8*)bits)[idx] & ~(bit*2-1);
|
||||
while (byte==0)
|
||||
{
|
||||
i = (i+8) & ~7;
|
||||
if (i>=MAX_TS_SET_SIZE)
|
||||
return;
|
||||
byte=((U8*)bits)[++idx];
|
||||
bit = 1;
|
||||
}
|
||||
byte = ((U8*)bits)[idx];
|
||||
while (bit & byte == 0)
|
||||
{
|
||||
bit <<= 1;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void TSIntegerSet::copy(const TSIntegerSet & otherSet)
|
||||
{
|
||||
dMemcpy(bits,otherSet.bits,MAX_TS_SET_DWORDS*4);
|
||||
}
|
||||
|
||||
void TSIntegerSet::insert(S32 index, bool value)
|
||||
{
|
||||
AssertFatal(index<MAX_TS_SET_SIZE,"TSIntegerSet::insert: out of range");
|
||||
|
||||
// shift bits in words after the insertion point
|
||||
U32 endWord = (end() >> 5) + 1;
|
||||
if (endWord >= MAX_TS_SET_DWORDS)
|
||||
endWord = MAX_TS_SET_DWORDS-1;
|
||||
|
||||
for (S32 i = endWord; i > (index >> 5); i--)
|
||||
{
|
||||
bits[i] = bits[i] << 1;
|
||||
if (bits[i-1] & 0x80000000)
|
||||
bits[i] |= 0x1;
|
||||
}
|
||||
|
||||
// shift to create space in target word
|
||||
U32 lowMask = (1 << (index & 0x1f)) - 1; // bits below the insert point
|
||||
U32 highMask = ~(lowMask | (1 << (index & 0x1f))); // bits above the insert point
|
||||
|
||||
S32 word = index >> 5;
|
||||
bits[word] = ((bits[word] << 1) & highMask) | (bits[word] & lowMask);
|
||||
|
||||
// insert new value
|
||||
if (value)
|
||||
set(index);
|
||||
}
|
||||
|
||||
void TSIntegerSet::erase(S32 index)
|
||||
{
|
||||
AssertFatal(index<MAX_TS_SET_SIZE,"TSIntegerSet::erase: out of range");
|
||||
|
||||
// shift to erase bit in target word
|
||||
S32 word = index >> 5;
|
||||
U32 lowMask = (1 << (index & 0x1f)) - 1; // bits below the erase point
|
||||
|
||||
bits[word] = ((bits[word] >> 1) & ~lowMask) | (bits[word] & lowMask);
|
||||
|
||||
// shift bits in words after the erase point
|
||||
U32 endWord = (end() >> 5) + 1;
|
||||
if (endWord >= MAX_TS_SET_DWORDS)
|
||||
endWord = MAX_TS_SET_DWORDS-1;
|
||||
|
||||
for (S32 i = (index >> 5) + 1; i <= endWord; i++)
|
||||
{
|
||||
if (bits[i] & 0x1)
|
||||
bits[i-1] |= 0x80000000;
|
||||
bits[i] = bits[i] >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
TSIntegerSet::TSIntegerSet()
|
||||
{
|
||||
clearAll();
|
||||
}
|
||||
|
||||
TSIntegerSet::TSIntegerSet(const TSIntegerSet & otherSet)
|
||||
{
|
||||
copy(otherSet);
|
||||
}
|
||||
|
||||
void TSIntegerSet::read(Stream * s)
|
||||
{
|
||||
clearAll();
|
||||
|
||||
S32 numInts;
|
||||
s->read(&numInts); // don't care about this
|
||||
|
||||
S32 sz;
|
||||
s->read(&sz);
|
||||
AssertFatal(sz<=MAX_TS_SET_DWORDS,"TSIntegerSet:: set too large...increase max set size and re-compile");
|
||||
|
||||
for (S32 i=0; i<sz; i++) // now mirrors the write code...
|
||||
s->read(&(bits[i]));
|
||||
}
|
||||
|
||||
void TSIntegerSet::write(Stream * s) const
|
||||
{
|
||||
s->write((S32)0); // don't do this anymore, keep in to avoid versioning
|
||||
S32 i,sz=0;
|
||||
for (i=0; i<MAX_TS_SET_DWORDS; i++)
|
||||
if (bits[i]!=0)
|
||||
sz=i+1;
|
||||
s->write(sz);
|
||||
for (i=0; i<sz; i++)
|
||||
s->write(bits[i]);
|
||||
}
|
||||
|
||||
119
Engine/source/ts/tsIntegerSet.h
Normal file
119
Engine/source/ts/tsIntegerSet.h
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSINTEGERSET_H_
|
||||
#define _TSINTEGERSET_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(TORQUE_MAX_LIB)
|
||||
#define MAX_TS_SET_DWORDS 32
|
||||
#else
|
||||
#define MAX_TS_SET_DWORDS 64
|
||||
#endif
|
||||
|
||||
#define MAX_TS_SET_SIZE (32*MAX_TS_SET_DWORDS)
|
||||
|
||||
class Stream;
|
||||
|
||||
/// The standard mathmatical set, where there are no duplicates. However,
|
||||
/// this set uses bits instead of numbers.
|
||||
class TSIntegerSet
|
||||
{
|
||||
/// The bits!
|
||||
U32 bits[MAX_TS_SET_DWORDS];
|
||||
|
||||
public:
|
||||
|
||||
/// Sets this bit to false
|
||||
void clear(S32 index);
|
||||
/// Set this bit to true
|
||||
void set(S32 index);
|
||||
/// Is this bit true?
|
||||
bool test(S32 index) const;
|
||||
|
||||
/// Sets all bits to false
|
||||
void clearAll(S32 upto = MAX_TS_SET_SIZE);
|
||||
/// Sets all bits to true
|
||||
void setAll(S32 upto = MAX_TS_SET_SIZE);
|
||||
/// Tests all bits for true
|
||||
bool testAll(S32 upto = MAX_TS_SET_SIZE) const;
|
||||
|
||||
/// Counts set bits
|
||||
S32 count(S32 upto = MAX_TS_SET_SIZE) const;
|
||||
|
||||
/// intersection (a & b)
|
||||
void intersect(const TSIntegerSet&);
|
||||
/// union (a | b)
|
||||
void overlap(const TSIntegerSet&);
|
||||
/// xor (a | b) & ( !(a & b) )
|
||||
void difference(const TSIntegerSet&);
|
||||
/// subtraction (a - b)
|
||||
void takeAway(const TSIntegerSet&);
|
||||
|
||||
/// copy one integer set into another
|
||||
void copy(const TSIntegerSet&);
|
||||
|
||||
void insert(S32 index, bool value);
|
||||
void erase(S32 index);
|
||||
|
||||
void operator=(const TSIntegerSet& otherSet) { copy(otherSet); }
|
||||
|
||||
S32 start() const;
|
||||
S32 end() const;
|
||||
void next(S32 & i) const;
|
||||
|
||||
void read(Stream *);
|
||||
void write(Stream *) const;
|
||||
|
||||
TSIntegerSet();
|
||||
TSIntegerSet(const TSIntegerSet&);
|
||||
};
|
||||
|
||||
inline void TSIntegerSet::clear(S32 index)
|
||||
{
|
||||
AssertFatal(index>=0 && index<MAX_TS_SET_SIZE,"TS::IntegerSet::clear");
|
||||
|
||||
bits[index>>5] &= ~(1 << (index & 31));
|
||||
}
|
||||
|
||||
inline void TSIntegerSet::set(S32 index)
|
||||
{
|
||||
AssertFatal(index>=0 && index<MAX_TS_SET_SIZE,"TS::IntegerSet::set");
|
||||
|
||||
bits[index>>5] |= 1 << (index & 31);
|
||||
}
|
||||
|
||||
inline bool TSIntegerSet::test(S32 index) const
|
||||
{
|
||||
AssertFatal(index>=0 && index<MAX_TS_SET_SIZE,"TS::IntegerSet::test");
|
||||
|
||||
return ((bits[index>>5] & (1 << (index & 31)))!=0);
|
||||
}
|
||||
|
||||
#endif
|
||||
553
Engine/source/ts/tsLastDetail.cpp
Normal file
553
Engine/source/ts/tsLastDetail.cpp
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/tsLastDetail.h"
|
||||
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "lighting/lightInfo.h"
|
||||
#include "renderInstance/renderImposterMgr.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "gfx/bitmap/ddsFile.h"
|
||||
#include "gfx/bitmap/ddsUtils.h"
|
||||
#include "gfx/gfxTextureManager.h"
|
||||
#include "math/mRandom.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "util/imposterCapture.h"
|
||||
#include "materials/materialManager.h"
|
||||
#include "materials/materialFeatureTypes.h"
|
||||
#include "console/consoleTypes.h"
|
||||
|
||||
|
||||
GFXImplementVertexFormat( ImposterState )
|
||||
{
|
||||
addElement( "POSITION", GFXDeclType_Float4 );
|
||||
|
||||
addElement( "ImposterParams", GFXDeclType_Float2, 0 );
|
||||
|
||||
addElement( "ImposterUpVec", GFXDeclType_Float3, 1 );
|
||||
addElement( "ImposterRightVec", GFXDeclType_Float3, 2 );
|
||||
};
|
||||
|
||||
|
||||
Vector<TSLastDetail*> TSLastDetail::smLastDetails;
|
||||
|
||||
bool TSLastDetail::smCanShadow = true;
|
||||
|
||||
|
||||
AFTER_MODULE_INIT( Sim )
|
||||
{
|
||||
Con::addVariable( "$pref::imposter::canShadow", TypeBool, &TSLastDetail::smCanShadow,
|
||||
"User preference which toggles shadows from imposters. Defaults to true.\n"
|
||||
"@ingroup Rendering\n" );
|
||||
}
|
||||
|
||||
|
||||
TSLastDetail::TSLastDetail( TSShape *shape,
|
||||
const String &cachePath,
|
||||
U32 numEquatorSteps,
|
||||
U32 numPolarSteps,
|
||||
F32 polarAngle,
|
||||
bool includePoles,
|
||||
S32 dl, S32 dim )
|
||||
{
|
||||
mNumEquatorSteps = getMax( numEquatorSteps, (U32)1 );
|
||||
mNumPolarSteps = numPolarSteps;
|
||||
mPolarAngle = polarAngle;
|
||||
mIncludePoles = includePoles;
|
||||
mShape = shape;
|
||||
mDl = dl;
|
||||
mDim = getMax( dim, (S32)32 );
|
||||
|
||||
mRadius = mShape->radius;
|
||||
mCenter = mShape->center;
|
||||
|
||||
mCachePath = cachePath;
|
||||
|
||||
mMaterial = NULL;
|
||||
mMatInstance = NULL;
|
||||
|
||||
// Store this in the static list.
|
||||
smLastDetails.push_back( this );
|
||||
}
|
||||
|
||||
TSLastDetail::~TSLastDetail()
|
||||
{
|
||||
SAFE_DELETE( mMatInstance );
|
||||
if ( mMaterial )
|
||||
mMaterial->deleteObject();
|
||||
|
||||
// Remove ourselves from the list.
|
||||
Vector<TSLastDetail*>::iterator iter = find( smLastDetails.begin(), smLastDetails.end(), this );
|
||||
smLastDetails.erase( iter );
|
||||
}
|
||||
|
||||
void TSLastDetail::render( const TSRenderState &rdata, F32 alpha )
|
||||
{
|
||||
// Early out if we have nothing to render.
|
||||
if ( alpha < 0.01f ||
|
||||
!mMatInstance ||
|
||||
mMaterial->mImposterUVs.size() == 0 )
|
||||
return;
|
||||
|
||||
const MatrixF &mat = GFX->getWorldMatrix();
|
||||
|
||||
// Post a render instance for this imposter... the special
|
||||
// imposter render manager will do the magic!
|
||||
RenderPassManager *renderPass = rdata.getSceneState()->getRenderPass();
|
||||
|
||||
ImposterRenderInst *ri = renderPass->allocInst<ImposterRenderInst>();
|
||||
ri->mat = rdata.getSceneState()->getOverrideMaterial( mMatInstance );
|
||||
|
||||
ri->state.alpha = alpha;
|
||||
|
||||
// Store the up and right vectors of the rotation
|
||||
// and we'll generate the up vector in the shader.
|
||||
//
|
||||
// This is faster than building a quat on the
|
||||
// CPU and then rebuilding the matrix on the GPU.
|
||||
//
|
||||
// NOTE: These vector include scale.
|
||||
//
|
||||
mat.getColumn( 2, &ri->state.upVec );
|
||||
mat.getColumn( 0, &ri->state.rightVec );
|
||||
|
||||
// We send the unscaled size and the vertex shader
|
||||
// will use the orientation vectors above to scale it.
|
||||
ri->state.halfSize = mRadius;
|
||||
|
||||
// We use the center of the object bounds for
|
||||
// the center of the billboard quad.
|
||||
mat.mulP( mCenter, &ri->state.center );
|
||||
|
||||
// We sort by the imposter type first so that RIT_Imposter and s
|
||||
// RIT_ImposterBatches do not get mixed together.
|
||||
//
|
||||
// We then sort by material.
|
||||
//
|
||||
ri->defaultKey = 1;
|
||||
ri->defaultKey2 = ri->mat->getStateHint();
|
||||
|
||||
renderPass->addInst( ri );
|
||||
}
|
||||
|
||||
void TSLastDetail::update( bool forceUpdate )
|
||||
{
|
||||
// This should never be called on a dedicated server or
|
||||
// anywhere else where we don't have a GFX device!
|
||||
AssertFatal( GFXDevice::devicePresent(), "TSLastDetail::update() - Cannot update without a GFX device!" );
|
||||
|
||||
// Clear the materialfirst.
|
||||
SAFE_DELETE( mMatInstance );
|
||||
if ( mMaterial )
|
||||
{
|
||||
mMaterial->deleteObject();
|
||||
mMaterial = NULL;
|
||||
}
|
||||
|
||||
// Make sure imposter textures have been flushed (and not just queued for deletion)
|
||||
TEXMGR->cleanupCache();
|
||||
|
||||
// Get the real path to the source shape for doing modified time
|
||||
// comparisons... this might be different if the DAEs have been
|
||||
// deleted from the install.
|
||||
String shapeFile( mCachePath );
|
||||
if ( !Platform::isFile( shapeFile ) )
|
||||
{
|
||||
Torque::Path path(shapeFile);
|
||||
path.setExtension("cached.dts");
|
||||
shapeFile = path.getFullPath();
|
||||
if ( !Platform::isFile( shapeFile ) )
|
||||
{
|
||||
Con::errorf( "TSLastDetail::update - '%s' could not be found!", mCachePath.c_str() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Do we need to update the imposter?
|
||||
const String diffuseMapPath = _getDiffuseMapPath();
|
||||
if ( forceUpdate ||
|
||||
Platform::compareModifiedTimes( diffuseMapPath, shapeFile ) <= 0 )
|
||||
_update();
|
||||
|
||||
// If the time check fails now then the update must have not worked.
|
||||
if ( Platform::compareModifiedTimes( diffuseMapPath, shapeFile ) < 0 )
|
||||
{
|
||||
Con::errorf( "TSLastDetail::update - Failed to create imposters for '%s'!", mCachePath.c_str() );
|
||||
return;
|
||||
}
|
||||
|
||||
// Figure out what our vertex format will be.
|
||||
//
|
||||
// If we're on SM 3.0 we can do multiple vertex streams
|
||||
// and the performance win is big as we send 3x less data
|
||||
// on each imposter instance.
|
||||
//
|
||||
// The problem is SM 2.0 won't do this, so we need to
|
||||
// support fallback to regular single stream imposters.
|
||||
//
|
||||
//mImposterVertDecl.copy( *getGFXVertexFormat<ImposterCorner>() );
|
||||
//mImposterVertDecl.append( *getGFXVertexFormat<ImposterState>(), 1 );
|
||||
//mImposterVertDecl.getDecl();
|
||||
mImposterVertDecl.clear();
|
||||
mImposterVertDecl.copy( *getGFXVertexFormat<ImposterState>() );
|
||||
|
||||
// Setup the material for this imposter.
|
||||
mMaterial = MATMGR->allocateAndRegister( String::EmptyString );
|
||||
mMaterial->mAutoGenerated = true;
|
||||
mMaterial->mDiffuseMapFilename[0] = diffuseMapPath;
|
||||
mMaterial->mNormalMapFilename[0] = _getNormalMapPath();
|
||||
mMaterial->mImposterLimits.set( (mNumPolarSteps * 2) + 1, mNumEquatorSteps, mPolarAngle, mIncludePoles );
|
||||
mMaterial->mTranslucent = true;
|
||||
mMaterial->mTranslucentBlendOp = Material::None;
|
||||
mMaterial->mTranslucentZWrite = true;
|
||||
mMaterial->mDoubleSided = true;
|
||||
mMaterial->mAlphaTest = true;
|
||||
mMaterial->mAlphaRef = 84;
|
||||
|
||||
// Create the material instance.
|
||||
FeatureSet features = MATMGR->getDefaultFeatures();
|
||||
features.addFeature( MFT_ImposterVert );
|
||||
mMatInstance = mMaterial->createMatInstance();
|
||||
if ( !mMatInstance->init( features, &mImposterVertDecl ) )
|
||||
{
|
||||
delete mMatInstance;
|
||||
mMatInstance = NULL;
|
||||
}
|
||||
|
||||
// Get the diffuse texture and from its size and
|
||||
// the imposter dimensions we can generate the UVs.
|
||||
GFXTexHandle diffuseTex( diffuseMapPath, &GFXDefaultStaticDiffuseProfile, String::EmptyString );
|
||||
Point2I texSize( diffuseTex->getWidth(), diffuseTex->getHeight() );
|
||||
|
||||
_validateDim();
|
||||
|
||||
S32 downscaledDim = mDim >> GFXTextureManager::getTextureDownscalePower(&GFXDefaultStaticDiffuseProfile);
|
||||
|
||||
// Ok... pack in bitmaps till we run out.
|
||||
Vector<RectF> imposterUVs;
|
||||
for ( S32 y=0; y+downscaledDim <= texSize.y; )
|
||||
{
|
||||
for ( S32 x=0; x+downscaledDim <= texSize.x; )
|
||||
{
|
||||
// Store the uv for later lookup.
|
||||
RectF info;
|
||||
info.point.set( (F32)x / (F32)texSize.x, (F32)y / (F32)texSize.y );
|
||||
info.extent.set( (F32)downscaledDim / (F32)texSize.x, (F32)downscaledDim / (F32)texSize.y );
|
||||
imposterUVs.push_back( info );
|
||||
|
||||
x += downscaledDim;
|
||||
}
|
||||
|
||||
y += downscaledDim;
|
||||
}
|
||||
|
||||
AssertFatal( imposterUVs.size() != 0, "hey" );
|
||||
|
||||
mMaterial->mImposterUVs = imposterUVs;
|
||||
}
|
||||
|
||||
void TSLastDetail::_validateDim()
|
||||
{
|
||||
// Loop till they fit.
|
||||
S32 newDim = mDim;
|
||||
while ( true )
|
||||
{
|
||||
S32 maxImposters = ( smMaxTexSize / newDim ) * ( smMaxTexSize / newDim );
|
||||
S32 imposterCount = ( ((2*mNumPolarSteps) + 1 ) * mNumEquatorSteps ) + ( mIncludePoles ? 2 : 0 );
|
||||
if ( imposterCount <= maxImposters )
|
||||
break;
|
||||
|
||||
// There are too many imposters to fit a single
|
||||
// texture, so we fail. These imposters are for
|
||||
// rendering small distant objects. If you need
|
||||
// a really high resolution imposter or many images
|
||||
// around the equator and poles, maybe you need a
|
||||
// custom solution.
|
||||
|
||||
newDim /= 2;
|
||||
}
|
||||
|
||||
if ( newDim != mDim )
|
||||
{
|
||||
Con::printf( "TSLastDetail::_validateDim - '%s' detail dimensions too big! Reduced from %d to %d.",
|
||||
mCachePath.c_str(),
|
||||
mDim, newDim );
|
||||
|
||||
mDim = newDim;
|
||||
}
|
||||
}
|
||||
|
||||
void TSLastDetail::_update()
|
||||
{
|
||||
// We're gonna render... make sure we can.
|
||||
bool sceneBegun = GFX->canCurrentlyRender();
|
||||
if ( !sceneBegun )
|
||||
GFX->beginScene();
|
||||
|
||||
_validateDim();
|
||||
|
||||
Vector<GBitmap*> bitmaps;
|
||||
Vector<GBitmap*> normalmaps;
|
||||
|
||||
// We need to create our own instance to render with.
|
||||
TSShapeInstance *shape = new TSShapeInstance( mShape, true );
|
||||
|
||||
// Animate the shape once.
|
||||
shape->animate( mDl );
|
||||
|
||||
// So we don't have to change it everywhere.
|
||||
const GFXFormat format = GFXFormatR8G8B8A8;
|
||||
|
||||
S32 imposterCount = ( ((2*mNumPolarSteps) + 1 ) * mNumEquatorSteps ) + ( mIncludePoles ? 2 : 0 );
|
||||
|
||||
// Figure out the optimal texture size.
|
||||
Point2I texSize( smMaxTexSize, smMaxTexSize );
|
||||
while ( true )
|
||||
{
|
||||
Point2I halfSize( texSize.x / 2, texSize.y / 2 );
|
||||
U32 count = ( halfSize.x / mDim ) * ( halfSize.y / mDim );
|
||||
if ( count < imposterCount )
|
||||
{
|
||||
// Try half of the height.
|
||||
count = ( texSize.x / mDim ) * ( halfSize.y / mDim );
|
||||
if ( count >= imposterCount )
|
||||
texSize.y = halfSize.y;
|
||||
break;
|
||||
}
|
||||
|
||||
texSize = halfSize;
|
||||
}
|
||||
|
||||
GBitmap *imposter = NULL;
|
||||
GBitmap *normalmap = NULL;
|
||||
GBitmap destBmp( texSize.x, texSize.y, true, format );
|
||||
GBitmap destNormal( texSize.x, texSize.y, true, format );
|
||||
|
||||
U32 mipLevels = destBmp.getNumMipLevels();
|
||||
|
||||
ImposterCapture *imposterCap = new ImposterCapture();
|
||||
|
||||
F32 equatorStepSize = M_2PI_F / (F32)mNumEquatorSteps;
|
||||
|
||||
static const MatrixF topXfm( EulerF( -M_PI_F / 2.0f, 0, 0 ) );
|
||||
static const MatrixF bottomXfm( EulerF( M_PI_F / 2.0f, 0, 0 ) );
|
||||
|
||||
MatrixF angMat;
|
||||
|
||||
F32 polarStepSize = 0.0f;
|
||||
if ( mNumPolarSteps > 0 )
|
||||
polarStepSize = -( 0.5f * M_PI_F - mDegToRad( mPolarAngle ) ) / (F32)mNumPolarSteps;
|
||||
|
||||
PROFILE_START(TSLastDetail_snapshots);
|
||||
|
||||
S32 currDim = mDim;
|
||||
for ( S32 mip = 0; mip < mipLevels; mip++ )
|
||||
{
|
||||
if ( currDim < 1 )
|
||||
currDim = 1;
|
||||
|
||||
dMemset( destBmp.getWritableBits(mip), 0, destBmp.getWidth(mip) * destBmp.getHeight(mip) * GFXFormat_getByteSize( format ) );
|
||||
dMemset( destNormal.getWritableBits(mip), 0, destNormal.getWidth(mip) * destNormal.getHeight(mip) * GFXFormat_getByteSize( format ) );
|
||||
|
||||
bitmaps.clear();
|
||||
normalmaps.clear();
|
||||
|
||||
F32 rotX = 0.0f;
|
||||
if ( mNumPolarSteps > 0 )
|
||||
rotX = -( mDegToRad( mPolarAngle ) - 0.5f * M_PI_F );
|
||||
|
||||
// We capture the images in a particular order which must
|
||||
// match the order expected by the imposter renderer.
|
||||
|
||||
imposterCap->begin( shape, mDl, currDim, mRadius, mCenter );
|
||||
|
||||
for ( U32 j=0; j < (2 * mNumPolarSteps + 1); j++ )
|
||||
{
|
||||
F32 rotZ = -M_PI_F / 2.0f;
|
||||
|
||||
for ( U32 k=0; k < mNumEquatorSteps; k++ )
|
||||
{
|
||||
angMat.mul( MatrixF( EulerF( rotX, 0, 0 ) ),
|
||||
MatrixF( EulerF( 0, 0, rotZ ) ) );
|
||||
|
||||
imposterCap->capture( angMat, &imposter, &normalmap );
|
||||
|
||||
bitmaps.push_back( imposter );
|
||||
normalmaps.push_back( normalmap );
|
||||
|
||||
rotZ += equatorStepSize;
|
||||
}
|
||||
|
||||
rotX += polarStepSize;
|
||||
|
||||
if ( mIncludePoles )
|
||||
{
|
||||
imposterCap->capture( topXfm, &imposter, &normalmap );
|
||||
|
||||
bitmaps.push_back(imposter);
|
||||
normalmaps.push_back( normalmap );
|
||||
|
||||
imposterCap->capture( bottomXfm, &imposter, &normalmap );
|
||||
|
||||
bitmaps.push_back( imposter );
|
||||
normalmaps.push_back( normalmap );
|
||||
}
|
||||
}
|
||||
|
||||
imposterCap->end();
|
||||
|
||||
Point2I texSize( destBmp.getWidth(mip), destBmp.getHeight(mip) );
|
||||
|
||||
// Ok... pack in bitmaps till we run out.
|
||||
for ( S32 y=0; y+currDim <= texSize.y; )
|
||||
{
|
||||
for ( S32 x=0; x+currDim <= texSize.x; )
|
||||
{
|
||||
// Copy the next bitmap to the dest texture.
|
||||
GBitmap* bmp = bitmaps.first();
|
||||
bitmaps.pop_front();
|
||||
destBmp.copyRect( bmp, RectI( 0, 0, currDim, currDim ), Point2I( x, y ), 0, mip );
|
||||
delete bmp;
|
||||
|
||||
// Copy the next normal to the dest texture.
|
||||
GBitmap* normalmap = normalmaps.first();
|
||||
normalmaps.pop_front();
|
||||
destNormal.copyRect( normalmap, RectI( 0, 0, currDim, currDim ), Point2I( x, y ), 0, mip );
|
||||
delete normalmap;
|
||||
|
||||
// Did we finish?
|
||||
if ( bitmaps.empty() )
|
||||
break;
|
||||
|
||||
x += currDim;
|
||||
}
|
||||
|
||||
// Did we finish?
|
||||
if ( bitmaps.empty() )
|
||||
break;
|
||||
|
||||
y += currDim;
|
||||
}
|
||||
|
||||
// Next mip...
|
||||
currDim /= 2;
|
||||
}
|
||||
|
||||
PROFILE_END(); // TSLastDetail_snapshots
|
||||
|
||||
delete imposterCap;
|
||||
delete shape;
|
||||
|
||||
|
||||
// Should we dump the images?
|
||||
if ( Con::getBoolVariable( "$TSLastDetail::dumpImposters", false ) )
|
||||
{
|
||||
String imposterPath = mCachePath + ".imposter.png";
|
||||
String normalsPath = mCachePath + ".imposter_normals.png";
|
||||
|
||||
FileStream stream;
|
||||
if ( stream.open( imposterPath, Torque::FS::File::Write ) )
|
||||
destBmp.writeBitmap( "png", stream );
|
||||
stream.close();
|
||||
|
||||
if ( stream.open( normalsPath, Torque::FS::File::Write ) )
|
||||
destNormal.writeBitmap( "png", stream );
|
||||
stream.close();
|
||||
}
|
||||
|
||||
// DEBUG: Some code to force usage of a test image.
|
||||
//GBitmap* tempMap = GBitmap::load( "./forest/data/test1234.png" );
|
||||
//tempMap->extrudeMipLevels();
|
||||
//mTexture.set( tempMap, &GFXDefaultStaticDiffuseProfile, false );
|
||||
//delete tempMap;
|
||||
|
||||
DDSFile *ddsDest = DDSFile::createDDSFileFromGBitmap( &destBmp );
|
||||
DDSUtil::squishDDS( ddsDest, GFXFormatDXT3 );
|
||||
|
||||
DDSFile *ddsNormals = DDSFile::createDDSFileFromGBitmap( &destNormal );
|
||||
DDSUtil::squishDDS( ddsNormals, GFXFormatDXT5 );
|
||||
|
||||
// Finally save the imposters to disk.
|
||||
FileStream fs;
|
||||
if ( fs.open( _getDiffuseMapPath(), Torque::FS::File::Write ) )
|
||||
{
|
||||
ddsDest->write( fs );
|
||||
fs.close();
|
||||
}
|
||||
if ( fs.open( _getNormalMapPath(), Torque::FS::File::Write ) )
|
||||
{
|
||||
ddsNormals->write( fs );
|
||||
fs.close();
|
||||
}
|
||||
|
||||
delete ddsDest;
|
||||
delete ddsNormals;
|
||||
|
||||
// If we did a begin then end it now.
|
||||
if ( !sceneBegun )
|
||||
GFX->endScene();
|
||||
}
|
||||
|
||||
void TSLastDetail::deleteImposterCacheTextures()
|
||||
{
|
||||
const String diffuseMap = _getDiffuseMapPath();
|
||||
if ( diffuseMap.length() )
|
||||
dFileDelete( diffuseMap );
|
||||
|
||||
const String normalMap = _getNormalMapPath();
|
||||
if ( normalMap.length() )
|
||||
dFileDelete( normalMap );
|
||||
}
|
||||
|
||||
void TSLastDetail::updateImposterImages( bool forceUpdate )
|
||||
{
|
||||
// Can't do it without GFX!
|
||||
if ( !GFXDevice::devicePresent() )
|
||||
return;
|
||||
|
||||
//D3DPERF_SetMarker( D3DCOLOR_RGBA( 0, 255, 0, 255 ), L"TSLastDetail::makeImposter" );
|
||||
|
||||
bool sceneBegun = GFX->canCurrentlyRender();
|
||||
if ( !sceneBegun )
|
||||
GFX->beginScene();
|
||||
|
||||
Vector<TSLastDetail*>::iterator iter = smLastDetails.begin();
|
||||
for ( ; iter != smLastDetails.end(); iter++ )
|
||||
(*iter)->update( forceUpdate );
|
||||
|
||||
if ( !sceneBegun )
|
||||
GFX->endScene();
|
||||
}
|
||||
|
||||
ConsoleFunction(tsUpdateImposterImages, void, 1, 2, "tsUpdateImposterImages( bool forceupdate )")
|
||||
{
|
||||
if ( argc > 1 )
|
||||
TSLastDetail::updateImposterImages( dAtob( argv[1] ) );
|
||||
else
|
||||
TSLastDetail::updateImposterImages();
|
||||
}
|
||||
|
||||
|
||||
207
Engine/source/ts/tsLastDetail.h
Normal file
207
Engine/source/ts/tsLastDetail.h
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSLASTDETAIL_H_
|
||||
#define _TSLASTDETAIL_H_
|
||||
|
||||
#ifndef _MATHTYPES_H_
|
||||
#include "math/mathTypes.h"
|
||||
#endif
|
||||
#ifndef _MPOINT3_H_
|
||||
#include "math/mPoint3.h"
|
||||
#endif
|
||||
#ifndef _MMATRIX_H_
|
||||
#include "math/mMatrix.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#endif
|
||||
#ifndef _TSRENDERDATA_H_
|
||||
#include "ts/tsRenderState.h"
|
||||
#endif
|
||||
#ifndef _GFXVERTEXFORMAT_H_
|
||||
#include "gfx/gfxVertexFormat.h"
|
||||
#endif
|
||||
#ifndef _SIM_H_
|
||||
#include "console/simObject.h"
|
||||
#endif
|
||||
|
||||
|
||||
class TSShape;
|
||||
class TSRenderState;
|
||||
class SceneRenderState;
|
||||
class Material;
|
||||
class BaseMatInstance;
|
||||
|
||||
|
||||
/// The imposter state vertex format.
|
||||
GFXDeclareVertexFormat( ImposterState )
|
||||
{
|
||||
/// .xyz = imposter center
|
||||
/// .w = billboard corner... damn SM 2.0
|
||||
Point3F center;
|
||||
float corner;
|
||||
|
||||
/// .x = scaled half size
|
||||
/// .y = alpha fade out
|
||||
float halfSize;
|
||||
float alpha;
|
||||
|
||||
/// The rotation encoded as the up
|
||||
/// and right vectors... cross FTW.
|
||||
Point3F upVec;
|
||||
Point3F rightVec;
|
||||
};
|
||||
|
||||
|
||||
/// This neat little class renders the object to a texture so that when the object
|
||||
/// is far away, it can be drawn as a billboard instead of a mesh. This happens
|
||||
/// when the model is first loaded as to keep the realtime render as fast as possible.
|
||||
/// It also renders the model from a few different perspectives so that it would actually
|
||||
/// pass as a model instead of a silly old billboard. In other words, this is an imposter.
|
||||
class TSLastDetail
|
||||
{
|
||||
protected:
|
||||
|
||||
/// The shape which we're impostering.
|
||||
TSShape *mShape;
|
||||
|
||||
/// This is the path of the object, which is
|
||||
/// where we'll be storing our cache for rendered imposters.
|
||||
String mCachePath;
|
||||
|
||||
/// The shape detail level to capture into
|
||||
/// the imposters.
|
||||
S32 mDl;
|
||||
|
||||
/// The bounding radius of the shape
|
||||
/// used to size the billboard.
|
||||
F32 mRadius;
|
||||
|
||||
/// The center offset for the bounding
|
||||
/// sphere used to render the imposter.
|
||||
Point3F mCenter;
|
||||
|
||||
/// The square dimensions of each
|
||||
/// captured imposter image.
|
||||
S32 mDim;
|
||||
|
||||
/// The number steps around the equator of
|
||||
/// the globe at which we capture an imposter.
|
||||
U32 mNumEquatorSteps;
|
||||
|
||||
/// The number of steps to go from equator to
|
||||
/// each polar region (0 means equator only) at
|
||||
/// which we capture an imposter.
|
||||
U32 mNumPolarSteps;
|
||||
|
||||
/// The angle in radians of sub-polar regions.
|
||||
F32 mPolarAngle;
|
||||
|
||||
/// If true we captures polar images in the
|
||||
/// imposter texture.
|
||||
bool mIncludePoles;
|
||||
|
||||
/// The combined imposter state and corner data vertex
|
||||
/// format used for rendering with multiple streams.
|
||||
GFXVertexFormat mImposterVertDecl;
|
||||
|
||||
/// The material for this imposter.
|
||||
SimObjectPtr<Material> mMaterial;
|
||||
|
||||
/// The material instance used to render this imposter.
|
||||
BaseMatInstance *mMatInstance;
|
||||
|
||||
/// This is a global list of all the TSLastDetail
|
||||
/// objects in the system.
|
||||
static Vector<TSLastDetail*> smLastDetails;
|
||||
|
||||
/// The maximum texture size for a billboard texture.
|
||||
static const U32 smMaxTexSize = 2048;
|
||||
|
||||
/// This update actually regenerates the imposter images.
|
||||
void _update();
|
||||
|
||||
///
|
||||
void _validateDim();
|
||||
|
||||
/// Helper which returns the imposter diffuse map path.
|
||||
String _getDiffuseMapPath() const { return mCachePath + ".imposter.dds"; }
|
||||
|
||||
/// Helper which returns the imposter normal map path.
|
||||
String _getNormalMapPath() const { return mCachePath + ".imposter_normals.dds"; }
|
||||
|
||||
public:
|
||||
|
||||
TSLastDetail( TSShape *shape,
|
||||
const String &cachePath,
|
||||
U32 numEquatorSteps,
|
||||
U32 numPolarSteps,
|
||||
F32 polarAngle,
|
||||
bool includePoles,
|
||||
S32 dl,
|
||||
S32 dim );
|
||||
|
||||
~TSLastDetail();
|
||||
|
||||
/// Global preference for rendering imposters to shadows.
|
||||
static bool smCanShadow;
|
||||
|
||||
/// Calls update on all TSLastDetail objects in the system.
|
||||
/// @see update()
|
||||
static void updateImposterImages( bool forceUpdate = false );
|
||||
|
||||
/// Loads the imposter images by reading them from the disk
|
||||
/// or generating them if the TSShape is more recient than the
|
||||
/// cached imposter textures.
|
||||
///
|
||||
/// This should not be called from within any rendering code.
|
||||
///
|
||||
/// @param forceUpdate If true the disk cache is invalidated and
|
||||
/// new imposter images are rendered.
|
||||
///
|
||||
void update( bool forceUpdate = false );
|
||||
|
||||
|
||||
/// Internal function called from TSShapeInstance to
|
||||
/// submit an imposter render instance.
|
||||
void render( const TSRenderState &rdata, F32 alpha );
|
||||
|
||||
/// Returns the material instance used to render this imposter.
|
||||
BaseMatInstance* getMatInstance() const { return mMatInstance; }
|
||||
|
||||
/// Helper function which deletes the cached imposter
|
||||
/// texture files from disk.
|
||||
void deleteImposterCacheTextures();
|
||||
|
||||
/// Returns the radius.
|
||||
/// @see mRadius
|
||||
F32 getRadius() const { return mRadius; }
|
||||
};
|
||||
|
||||
|
||||
#endif // _TSLASTDETAIL_H_
|
||||
|
||||
|
||||
323
Engine/source/ts/tsMaterialList.cpp
Normal file
323
Engine/source/ts/tsMaterialList.cpp
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/tsMaterialList.h"
|
||||
|
||||
#include "ts/tsShape.h"
|
||||
#include "materials/matInstance.h"
|
||||
#include "materials/materialManager.h"
|
||||
|
||||
|
||||
TSMaterialList::TSMaterialList(U32 materialCount,
|
||||
const char **materialNames,
|
||||
const U32 * materialFlags,
|
||||
const U32 * reflectanceMaps,
|
||||
const U32 * bumpMaps,
|
||||
const U32 * detailMaps,
|
||||
const F32 * detailScales,
|
||||
const F32 * reflectionAmounts)
|
||||
: MaterialList(materialCount,materialNames),
|
||||
mNamesTransformed(false)
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mFlags);
|
||||
VECTOR_SET_ASSOCIATION(mReflectanceMaps);
|
||||
VECTOR_SET_ASSOCIATION(mBumpMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailScales);
|
||||
VECTOR_SET_ASSOCIATION(mReflectionAmounts);
|
||||
|
||||
allocate(materialCount);
|
||||
|
||||
dMemcpy(mFlags.address(),materialFlags,materialCount*sizeof(U32));
|
||||
dMemcpy(mReflectanceMaps.address(),reflectanceMaps,materialCount*sizeof(U32));
|
||||
dMemcpy(mBumpMaps.address(),bumpMaps,materialCount*sizeof(U32));
|
||||
dMemcpy(mDetailMaps.address(),detailMaps,materialCount*sizeof(U32));
|
||||
dMemcpy(mDetailScales.address(),detailScales,materialCount*sizeof(F32));
|
||||
dMemcpy(mReflectionAmounts.address(),reflectionAmounts,materialCount*sizeof(F32));
|
||||
}
|
||||
|
||||
TSMaterialList::TSMaterialList()
|
||||
: mNamesTransformed(false)
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mFlags);
|
||||
VECTOR_SET_ASSOCIATION(mReflectanceMaps);
|
||||
VECTOR_SET_ASSOCIATION(mBumpMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailScales);
|
||||
VECTOR_SET_ASSOCIATION(mReflectionAmounts);
|
||||
}
|
||||
|
||||
TSMaterialList::TSMaterialList(const TSMaterialList* pCopy)
|
||||
: MaterialList(pCopy)
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mFlags);
|
||||
VECTOR_SET_ASSOCIATION(mReflectanceMaps);
|
||||
VECTOR_SET_ASSOCIATION(mBumpMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailScales);
|
||||
VECTOR_SET_ASSOCIATION(mReflectionAmounts);
|
||||
|
||||
mFlags = pCopy->mFlags;
|
||||
mReflectanceMaps = pCopy->mReflectanceMaps;
|
||||
mBumpMaps = pCopy->mBumpMaps;
|
||||
mDetailMaps = pCopy->mDetailMaps;
|
||||
mDetailScales = pCopy->mDetailScales;
|
||||
mReflectionAmounts = pCopy->mReflectionAmounts;
|
||||
mNamesTransformed = pCopy->mNamesTransformed;
|
||||
}
|
||||
|
||||
TSMaterialList::~TSMaterialList()
|
||||
{
|
||||
free();
|
||||
}
|
||||
|
||||
void TSMaterialList::free()
|
||||
{
|
||||
// these aren't found on our parent, clear them out here to keep in synch
|
||||
mFlags.clear();
|
||||
mReflectanceMaps.clear();
|
||||
mBumpMaps.clear();
|
||||
mDetailMaps.clear();
|
||||
mDetailScales.clear();
|
||||
mReflectionAmounts.clear();
|
||||
|
||||
Parent::free();
|
||||
}
|
||||
|
||||
void TSMaterialList::push_back(const String &name, U32 flags, U32 rMap, U32 bMap, U32 dMap, F32 dScale, F32 emapAmount)
|
||||
{
|
||||
Parent::push_back(name);
|
||||
mFlags.push_back(flags);
|
||||
if (rMap==0xFFFFFFFF)
|
||||
mReflectanceMaps.push_back(size()-1);
|
||||
else
|
||||
mReflectanceMaps.push_back(rMap);
|
||||
mBumpMaps.push_back(bMap);
|
||||
mDetailMaps.push_back(dMap);
|
||||
mDetailScales.push_back(dScale);
|
||||
mReflectionAmounts.push_back(emapAmount);
|
||||
}
|
||||
|
||||
void TSMaterialList::push_back(const char * name, U32 flags, Material* mat)
|
||||
{
|
||||
Parent::push_back(name, mat);
|
||||
mFlags.push_back(flags);
|
||||
mReflectanceMaps.push_back(size()-1);
|
||||
mBumpMaps.push_back(0xFFFFFFFF);
|
||||
mDetailMaps.push_back(0xFFFFFFFF);
|
||||
mDetailScales.push_back(1.0f);
|
||||
mReflectionAmounts.push_back(1.0f);
|
||||
}
|
||||
|
||||
void TSMaterialList::allocate(U32 sz)
|
||||
{
|
||||
mFlags.setSize(sz);
|
||||
mReflectanceMaps.setSize(sz);
|
||||
mBumpMaps.setSize(sz);
|
||||
mDetailMaps.setSize(sz);
|
||||
mDetailScales.setSize(sz);
|
||||
mReflectionAmounts.setSize(sz);
|
||||
}
|
||||
|
||||
U32 TSMaterialList::getFlags(U32 index)
|
||||
{
|
||||
AssertFatal(index < size(),"TSMaterialList::getFlags: index out of range");
|
||||
return mFlags[index];
|
||||
}
|
||||
|
||||
void TSMaterialList::setFlags(U32 index, U32 value)
|
||||
{
|
||||
AssertFatal(index < size(),"TSMaterialList::getFlags: index out of range");
|
||||
mFlags[index] = value;
|
||||
}
|
||||
|
||||
bool TSMaterialList::write(Stream & s)
|
||||
{
|
||||
if (!Parent::write(s))
|
||||
return false;
|
||||
|
||||
U32 i;
|
||||
for (i=0; i<size(); i++)
|
||||
s.write(mFlags[i]);
|
||||
|
||||
for (i=0; i<size(); i++)
|
||||
s.write(mReflectanceMaps[i]);
|
||||
|
||||
for (i=0; i<size(); i++)
|
||||
s.write(mBumpMaps[i]);
|
||||
|
||||
for (i=0; i<size(); i++)
|
||||
s.write(mDetailMaps[i]);
|
||||
|
||||
// MDF - This used to write mLightmaps
|
||||
// We never ended up using it but it is
|
||||
// still part of the version 25 standard
|
||||
if (TSShape::smVersion == 25)
|
||||
{
|
||||
for (i=0; i<size(); i++)
|
||||
s.write(0xFFFFFFFF);
|
||||
}
|
||||
|
||||
for (i=0; i<size(); i++)
|
||||
s.write(mDetailScales[i]);
|
||||
|
||||
for (i=0; i<size(); i++)
|
||||
s.write(mReflectionAmounts[i]);
|
||||
|
||||
return (s.getStatus() == Stream::Ok);
|
||||
}
|
||||
|
||||
bool TSMaterialList::read(Stream & s)
|
||||
{
|
||||
if (!Parent::read(s))
|
||||
return false;
|
||||
|
||||
allocate(size());
|
||||
|
||||
U32 i;
|
||||
if (TSShape::smReadVersion < 2)
|
||||
{
|
||||
for (i=0; i<size(); i++)
|
||||
setFlags(i,S_Wrap|T_Wrap);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=0; i<size(); i++)
|
||||
s.read(&mFlags[i]);
|
||||
}
|
||||
|
||||
if (TSShape::smReadVersion < 5)
|
||||
{
|
||||
for (i=0; i<size(); i++)
|
||||
{
|
||||
mReflectanceMaps[i] = i;
|
||||
mBumpMaps[i] = 0xFFFFFFFF;
|
||||
mDetailMaps[i] = 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=0; i<size(); i++)
|
||||
s.read(&mReflectanceMaps[i]);
|
||||
for (i=0; i<size(); i++)
|
||||
s.read(&mBumpMaps[i]);
|
||||
for (i=0; i<size(); i++)
|
||||
s.read(&mDetailMaps[i]);
|
||||
|
||||
if (TSShape::smReadVersion == 25)
|
||||
{
|
||||
U32 dummy = 0;
|
||||
|
||||
for (i=0; i<size(); i++)
|
||||
s.read(&dummy);
|
||||
}
|
||||
}
|
||||
|
||||
if (TSShape::smReadVersion > 11)
|
||||
{
|
||||
for (i=0; i<size(); i++)
|
||||
s.read(&mDetailScales[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=0; i<size(); i++)
|
||||
mDetailScales[i] = 1.0f;
|
||||
}
|
||||
|
||||
if (TSShape::smReadVersion > 20)
|
||||
{
|
||||
for (i=0; i<size(); i++)
|
||||
s.read(&mReflectionAmounts[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=0; i<size(); i++)
|
||||
mReflectionAmounts[i] = 1.0f;
|
||||
}
|
||||
|
||||
if (TSShape::smReadVersion < 16)
|
||||
{
|
||||
// make sure emapping is off for translucent materials on old shapes
|
||||
for (i=0; i<size(); i++)
|
||||
if (mFlags[i] & TSMaterialList::Translucent)
|
||||
mFlags[i] |= TSMaterialList::NeverEnvMap;
|
||||
}
|
||||
|
||||
return (s.getStatus() == Stream::Ok);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Sets the specified material in the list to the specified texture. also
|
||||
// remaps mat instances based on the new texture name. Returns false if
|
||||
// the specified texture is not valid.
|
||||
//--------------------------------------------------------------------------
|
||||
bool TSMaterialList::renameMaterial(U32 i, const String& newName)
|
||||
{
|
||||
if (i > size() || newName.isEmpty())
|
||||
return false;
|
||||
|
||||
// Check if already using this name
|
||||
if (newName.equal(mMaterialNames[i], String::NoCase))
|
||||
{
|
||||
// same material, return true since we aren't changing it
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow the rename if the new name is mapped, or if there is a diffuse texture
|
||||
// available (for which a simple Material can be generated in mapMaterial)
|
||||
String mappedName = MATMGR->getMapEntry(newName);
|
||||
if (mappedName.isEmpty())
|
||||
{
|
||||
GFXTexHandle texHandle;
|
||||
if (mLookupPath.isEmpty())
|
||||
{
|
||||
texHandle.set( newName, &GFXDefaultStaticDiffuseProfile, avar("%s() - handle (line %d)", __FUNCTION__, __LINE__) );
|
||||
}
|
||||
else
|
||||
{
|
||||
String fullPath = String::ToString( "%s/%s", mLookupPath.c_str(), newName.c_str() );
|
||||
texHandle.set( fullPath, &GFXDefaultStaticDiffuseProfile, avar("%s() - handle (line %d)", __FUNCTION__, __LINE__) );
|
||||
}
|
||||
if (!texHandle.isValid())
|
||||
return false;
|
||||
}
|
||||
|
||||
// change material name
|
||||
mMaterialNames[i] = newName;
|
||||
|
||||
// Dump the old mat instance and remap the material.
|
||||
if( mMatInstList[ i ] )
|
||||
SAFE_DELETE( mMatInstList[ i ] );
|
||||
mapMaterial( i );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TSMaterialList::mapMaterial( U32 i )
|
||||
{
|
||||
Parent::mapMaterial( i );
|
||||
|
||||
BaseMatInstance* matInst = mMatInstList[i];
|
||||
if (matInst && matInst->getMaterial()->isTranslucent())
|
||||
mFlags[i] |= Translucent;
|
||||
}
|
||||
98
Engine/source/ts/tsMaterialList.h
Normal file
98
Engine/source/ts/tsMaterialList.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSMATERIALLIST_H_
|
||||
#define _TSMATERIALLIST_H_
|
||||
|
||||
#ifndef _MATERIALLIST_H_
|
||||
#include "materials/materialList.h"
|
||||
#endif
|
||||
#ifndef _PATH_H_
|
||||
#include "core/util/path.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// Specialized material list for 3space objects.
|
||||
class TSMaterialList : public MaterialList
|
||||
{
|
||||
typedef MaterialList Parent;
|
||||
|
||||
Vector<U32> mFlags;
|
||||
Vector<U32> mReflectanceMaps;
|
||||
Vector<U32> mBumpMaps;
|
||||
Vector<U32> mDetailMaps;
|
||||
Vector<F32> mDetailScales;
|
||||
Vector<F32> mReflectionAmounts;
|
||||
|
||||
bool mNamesTransformed;
|
||||
|
||||
void allocate(U32 sz);
|
||||
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
S_Wrap = BIT(0),
|
||||
T_Wrap = BIT(1),
|
||||
Translucent = BIT(2),
|
||||
Additive = BIT(3),
|
||||
Subtractive = BIT(4),
|
||||
SelfIlluminating = BIT(5),
|
||||
NeverEnvMap = BIT(6),
|
||||
NoMipMap = BIT(7),
|
||||
MipMap_ZeroBorder = BIT(8),
|
||||
AuxiliaryMap = BIT(27) | BIT(28) | BIT(29) | BIT(30) | BIT(31) // DEPRECATED
|
||||
};
|
||||
|
||||
TSMaterialList(U32 materialCount, const char **materialNames, const U32 * materialFlags,
|
||||
const U32 * reflectanceMaps, const U32 * bumpMaps, const U32 * detailMaps,
|
||||
const F32 * detailScales, const F32 * reflectionAmounts);
|
||||
TSMaterialList();
|
||||
TSMaterialList(const TSMaterialList*);
|
||||
~TSMaterialList();
|
||||
void free();
|
||||
|
||||
U32 getFlags(U32 index);
|
||||
void setFlags(U32 index, U32 value);
|
||||
|
||||
bool renameMaterial(U32 index, const String& newName); // use to support reskinning
|
||||
|
||||
/// pre-load only
|
||||
void push_back(const String &name, U32 flags,
|
||||
U32 a=0xFFFFFFFF, U32 b=0xFFFFFFFF, U32 c=0xFFFFFFFF,
|
||||
F32 dm=1.0f, F32 em=1.0f);
|
||||
void push_back(const char * name, U32 flags, Material* mat);
|
||||
|
||||
/// @name IO
|
||||
/// Functions for reading/writing to/from streams
|
||||
/// @{
|
||||
|
||||
bool write(Stream &);
|
||||
bool read(Stream &);
|
||||
/// @}
|
||||
|
||||
protected:
|
||||
virtual void mapMaterial( U32 index );
|
||||
};
|
||||
|
||||
|
||||
#endif // _TSMATERIALLIST_H_
|
||||
3043
Engine/source/ts/tsMesh.cpp
Normal file
3043
Engine/source/ts/tsMesh.cpp
Normal file
File diff suppressed because it is too large
Load diff
547
Engine/source/ts/tsMesh.h
Normal file
547
Engine/source/ts/tsMesh.h
Normal file
|
|
@ -0,0 +1,547 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSMESH_H_
|
||||
#define _TSMESH_H_
|
||||
|
||||
#ifndef _STREAM_H_
|
||||
#include "core/stream/stream.h"
|
||||
#endif
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef _ABSTRACTPOLYLIST_H_
|
||||
#include "collision/abstractPolyList.h"
|
||||
#endif
|
||||
#ifndef _GFXDEVICE_H_
|
||||
#include "gfx/gfxDevice.h"
|
||||
#endif
|
||||
#ifndef _GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
#ifndef _TSPARSEARRAY_H_
|
||||
#include "core/tSparseArray.h"
|
||||
#endif
|
||||
|
||||
#include "core/util/safeDelete.h"
|
||||
|
||||
#if defined(TORQUE_OS_XENON)
|
||||
//# define USE_MEM_VERTEX_BUFFERS
|
||||
#endif
|
||||
|
||||
#if defined(USE_MEM_VERTEX_BUFFERS)
|
||||
# include "gfx/D3D9/360/gfx360MemVertexBuffer.h"
|
||||
#endif
|
||||
|
||||
namespace Opcode { class Model; class MeshInterface; }
|
||||
namespace IceMaths { class IndexedTriangle; class Point; }
|
||||
|
||||
class Convex;
|
||||
|
||||
class SceneRenderState;
|
||||
class SceneObject;
|
||||
struct MeshRenderInst;
|
||||
class TSRenderState;
|
||||
class RenderPassManager;
|
||||
class TSMaterialList;
|
||||
class TSShapeInstance;
|
||||
struct RayInfo;
|
||||
class ConvexFeature;
|
||||
class ShapeBase;
|
||||
|
||||
struct TSDrawPrimitive
|
||||
{
|
||||
enum
|
||||
{
|
||||
Triangles = 0 << 30, ///< bits 30 and 31 index element type
|
||||
Strip = 1 << 30, ///< bits 30 and 31 index element type
|
||||
Fan = 2 << 30, ///< bits 30 and 31 index element type
|
||||
Indexed = BIT(29), ///< use glDrawElements if indexed, glDrawArrays o.w.
|
||||
NoMaterial = BIT(28), ///< set if no material (i.e., texture missing)
|
||||
MaterialMask = ~(Strip|Fan|Triangles|Indexed|NoMaterial),
|
||||
TypeMask = Strip|Fan|Triangles
|
||||
};
|
||||
|
||||
S32 start;
|
||||
S32 numElements;
|
||||
S32 matIndex; ///< holds material index & element type (see above enum)
|
||||
};
|
||||
|
||||
#if defined(USE_MEM_VERTEX_BUFFERS)
|
||||
struct __NullVertexStruct {};
|
||||
typedef GFX360MemVertexBufferHandle<__NullVertexStruct> TSVertexBufferHandle;
|
||||
#else
|
||||
typedef GFXVertexBufferDataHandle TSVertexBufferHandle;
|
||||
#endif
|
||||
|
||||
|
||||
///
|
||||
class TSMesh
|
||||
{
|
||||
friend class TSShape;
|
||||
public:
|
||||
struct TSMeshVertexArray;
|
||||
protected:
|
||||
|
||||
U32 meshType;
|
||||
Box3F mBounds;
|
||||
Point3F mCenter;
|
||||
F32 mRadius;
|
||||
F32 mVisibility;
|
||||
bool mDynamic;
|
||||
|
||||
const GFXVertexFormat *mVertexFormat;
|
||||
|
||||
U32 mVertSize;
|
||||
|
||||
TSVertexBufferHandle mVB;
|
||||
GFXPrimitiveBufferHandle mPB;
|
||||
|
||||
void _convertToAlignedMeshData( TSMeshVertexArray &vertexData, const Vector<Point3F> &_verts, const Vector<Point3F> &_norms );
|
||||
void _createVBIB( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb );
|
||||
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
/// types...
|
||||
StandardMeshType = 0,
|
||||
SkinMeshType = 1,
|
||||
DecalMeshType = 2,
|
||||
SortedMeshType = 3,
|
||||
NullMeshType = 4,
|
||||
TypeMask = StandardMeshType|SkinMeshType|DecalMeshType|SortedMeshType|NullMeshType,
|
||||
|
||||
/// flags (stored with meshType)...
|
||||
Billboard = BIT(31), HasDetailTexture = BIT(30),
|
||||
BillboardZAxis = BIT(29), UseEncodedNormals = BIT(28),
|
||||
FlagMask = Billboard|BillboardZAxis|HasDetailTexture|UseEncodedNormals
|
||||
};
|
||||
|
||||
U32 getMeshType() const { return meshType & TypeMask; }
|
||||
void setFlags(U32 flag) { meshType |= flag; }
|
||||
void clearFlags(U32 flag) { meshType &= ~flag; }
|
||||
U32 getFlags( U32 flag = 0xFFFFFFFF ) const { return meshType & flag; }
|
||||
|
||||
const Point3F* getNormals( S32 firstVert );
|
||||
|
||||
S32 parentMesh; ///< index into shapes mesh list
|
||||
S32 numFrames;
|
||||
S32 numMatFrames;
|
||||
S32 vertsPerFrame;
|
||||
|
||||
/// @name Aligned Vertex Data
|
||||
/// @{
|
||||
#pragma pack(1)
|
||||
struct __TSMeshVertexBase
|
||||
{
|
||||
Point3F _vert;
|
||||
F32 _tangentW;
|
||||
Point3F _normal;
|
||||
Point3F _tangent;
|
||||
Point2F _tvert;
|
||||
|
||||
const Point3F &vert() const { return _vert; }
|
||||
void vert(const Point3F &v) { _vert = v; }
|
||||
|
||||
const Point3F &normal() const { return _normal; }
|
||||
void normal(const Point3F &n) { _normal = n; }
|
||||
|
||||
Point4F tangent() const { return Point4F(_tangent.x, _tangent.y, _tangent.z, _tangentW); }
|
||||
void tangent(const Point4F &t) { _tangent = t.asPoint3F(); _tangentW = t.w; }
|
||||
|
||||
const Point2F &tvert() const { return _tvert; }
|
||||
void tvert(const Point2F &tv) { _tvert = tv;}
|
||||
|
||||
// Don't call these unless it's actually a __TSMeshVertex_3xUVColor, for real.
|
||||
// We don't want a vftable for virtual methods.
|
||||
Point2F &tvert2() const { return *reinterpret_cast<Point2F *>(reinterpret_cast<U8 *>(const_cast<__TSMeshVertexBase *>(this)) + 0x30); }
|
||||
void tvert2(const Point2F &tv) { (*reinterpret_cast<Point2F *>(reinterpret_cast<U8 *>(this) + 0x30)) = tv; }
|
||||
|
||||
GFXVertexColor &color() const { return *reinterpret_cast<GFXVertexColor *>(reinterpret_cast<U8 *>(const_cast<__TSMeshVertexBase *>(this)) + 0x38); }
|
||||
void color(const GFXVertexColor &c) { (*reinterpret_cast<GFXVertexColor *>(reinterpret_cast<U8 *>(this) + 0x38)) = c; }
|
||||
};
|
||||
|
||||
struct __TSMeshVertex_3xUVColor : public __TSMeshVertexBase
|
||||
{
|
||||
Point2F _tvert2;
|
||||
GFXVertexColor _color;
|
||||
F32 _tvert3; // Unused, but needed for alignment purposes
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
struct TSMeshVertexArray
|
||||
{
|
||||
protected:
|
||||
U8 *base;
|
||||
dsize_t vertSz;
|
||||
bool vertexDataReady;
|
||||
U32 numElements;
|
||||
|
||||
public:
|
||||
TSMeshVertexArray() : base(NULL), vertexDataReady(false), numElements(0) {}
|
||||
virtual ~TSMeshVertexArray() { set(NULL, 0, 0); }
|
||||
|
||||
virtual void set(void *b, dsize_t s, U32 n, bool autoFree = true )
|
||||
{
|
||||
if(base && autoFree)
|
||||
dFree_aligned(base);
|
||||
base = reinterpret_cast<U8 *>(b);
|
||||
vertSz = s;
|
||||
numElements = n;
|
||||
}
|
||||
|
||||
// Vector-like interface
|
||||
__TSMeshVertexBase &operator[](int idx) const { AssertFatal(idx < numElements, "Out of bounds access!"); return *reinterpret_cast<__TSMeshVertexBase *>(base + idx * vertSz); }
|
||||
__TSMeshVertexBase *address() const { return reinterpret_cast<__TSMeshVertexBase *>(base); }
|
||||
U32 size() const { return numElements; }
|
||||
dsize_t mem_size() const { return numElements * vertSz; }
|
||||
dsize_t vertSize() const { return vertSz; }
|
||||
bool isReady() const { return vertexDataReady; }
|
||||
void setReady(bool r) { vertexDataReady = r; }
|
||||
};
|
||||
|
||||
bool mHasColor;
|
||||
bool mHasTVert2;
|
||||
|
||||
TSMeshVertexArray mVertexData;
|
||||
dsize_t mNumVerts;
|
||||
virtual void convertToAlignedMeshData();
|
||||
/// @}
|
||||
|
||||
/// @name Vertex data
|
||||
/// @{
|
||||
|
||||
template<class T>
|
||||
class FreeableVector : public Vector<T>
|
||||
{
|
||||
public:
|
||||
bool free_memory() { return Vector<T>::resize(0); }
|
||||
|
||||
FreeableVector<T>& operator=(const Vector<T>& p) { Vector<T>::operator=(p); return *this; }
|
||||
FreeableVector<T>& operator=(const FreeableVector<T>& p) { Vector<T>::operator=(p); return *this; }
|
||||
};
|
||||
|
||||
FreeableVector<Point3F> verts;
|
||||
FreeableVector<Point3F> norms;
|
||||
FreeableVector<Point2F> tverts;
|
||||
FreeableVector<Point4F> tangents;
|
||||
|
||||
// Optional second texture uvs.
|
||||
FreeableVector<Point2F> tverts2;
|
||||
|
||||
// Optional vertex colors data.
|
||||
FreeableVector<ColorI> colors;
|
||||
/// @}
|
||||
|
||||
Vector<TSDrawPrimitive> primitives;
|
||||
Vector<U8> encodedNorms;
|
||||
Vector<U32> indices;
|
||||
|
||||
/// billboard data
|
||||
Point3F billboardAxis;
|
||||
|
||||
/// @name Convex Hull Data
|
||||
/// Convex hulls are convex (no angles >= 180º) meshes used for collision
|
||||
/// @{
|
||||
|
||||
Vector<Point3F> planeNormals;
|
||||
Vector<F32> planeConstants;
|
||||
Vector<U32> planeMaterials;
|
||||
S32 planesPerFrame;
|
||||
U32 mergeBufferStart;
|
||||
/// @}
|
||||
|
||||
/// @name Render Methods
|
||||
/// @{
|
||||
|
||||
/// This is used by sgShadowProjector to render the
|
||||
/// mesh directly, skipping the render manager.
|
||||
virtual void render( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb );
|
||||
void innerRender( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb );
|
||||
virtual void render( TSMaterialList *,
|
||||
const TSRenderState &data,
|
||||
bool isSkinDirty,
|
||||
const Vector<MatrixF> &transforms,
|
||||
TSVertexBufferHandle &vertexBuffer,
|
||||
GFXPrimitiveBufferHandle &primitiveBuffer );
|
||||
|
||||
void innerRender( TSMaterialList *, const TSRenderState &data, TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Material Methods
|
||||
/// @{
|
||||
void setFade( F32 fade ) { mVisibility = fade; }
|
||||
void clearFade() { setFade( 1.0f ); }
|
||||
/// @}
|
||||
|
||||
/// @name Collision Methods
|
||||
/// @{
|
||||
|
||||
virtual bool buildPolyList( S32 frame, AbstractPolyList * polyList, U32 & surfaceKey, TSMaterialList* materials );
|
||||
virtual bool getFeatures( S32 frame, const MatrixF&, const VectorF&, ConvexFeature*, U32 &surfaceKey );
|
||||
virtual void support( S32 frame, const Point3F &v, F32 *currMaxDP, Point3F *currSupport );
|
||||
virtual bool castRay( S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials );
|
||||
virtual bool castRayRendered( S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials );
|
||||
virtual bool buildConvexHull(); ///< returns false if not convex (still builds planes)
|
||||
bool addToHull( U32 idx0, U32 idx1, U32 idx2 );
|
||||
/// @}
|
||||
|
||||
/// @name Bounding Methods
|
||||
/// calculate and get bounding information
|
||||
/// @{
|
||||
|
||||
void computeBounds();
|
||||
virtual void computeBounds( const MatrixF &transform, Box3F &bounds, S32 frame = 0, Point3F *center = NULL, F32 *radius = NULL );
|
||||
void computeBounds( const Point3F *, S32 numVerts, S32 stride, const MatrixF &transform, Box3F &bounds, Point3F *center, F32 *radius );
|
||||
const Box3F& getBounds() const { return mBounds; }
|
||||
const Point3F& getCenter() const { return mCenter; }
|
||||
F32 getRadius() const { return mRadius; }
|
||||
virtual S32 getNumPolys() const;
|
||||
|
||||
static U8 encodeNormal( const Point3F &normal );
|
||||
static const Point3F& decodeNormal( U8 ncode ) { return smU8ToNormalTable[ncode]; }
|
||||
/// @}
|
||||
|
||||
/// persist methods...
|
||||
virtual void assemble( bool skip );
|
||||
static TSMesh* assembleMesh( U32 meshType, bool skip );
|
||||
virtual void disassemble();
|
||||
|
||||
void createVBIB();
|
||||
void createTangents(const Vector<Point3F> &_verts, const Vector<Point3F> &_norms);
|
||||
void findTangent( U32 index1,
|
||||
U32 index2,
|
||||
U32 index3,
|
||||
Point3F *tan0,
|
||||
Point3F *tan1,
|
||||
const Vector<Point3F> &_verts);
|
||||
|
||||
/// on load...optionally convert primitives to other form
|
||||
static bool smUseTriangles;
|
||||
static bool smUseOneStrip;
|
||||
static S32 smMinStripSize;
|
||||
static bool smUseEncodedNormals;
|
||||
|
||||
/// Enables mesh instancing on non-skin meshes that
|
||||
/// have less that this count of verts.
|
||||
static S32 smMaxInstancingVerts;
|
||||
|
||||
/// convert primitives on load...
|
||||
void convertToTris(const TSDrawPrimitive *primitivesIn, const S32 *indicesIn,
|
||||
S32 numPrimIn, S32 & numPrimOut, S32 & numIndicesOut,
|
||||
TSDrawPrimitive *primitivesOut, S32 *indicesOut) const;
|
||||
void convertToSingleStrip(const TSDrawPrimitive *primitivesIn, const S32 *indicesIn,
|
||||
S32 numPrimIn, S32 &numPrimOut, S32 &numIndicesOut,
|
||||
TSDrawPrimitive *primitivesOut, S32 *indicesOut) const;
|
||||
void leaveAsMultipleStrips(const TSDrawPrimitive *primitivesIn, const S32 *indicesIn,
|
||||
S32 numPrimIn, S32 &numPrimOut, S32 &numIndicesOut,
|
||||
TSDrawPrimitive *primitivesOut, S32 *indicesOut) const;
|
||||
|
||||
/// methods used during assembly to share vertexand other info
|
||||
/// between meshes (and for skipping detail levels on load)
|
||||
S32* getSharedData32( S32 parentMesh, S32 size, S32 **source, bool skip );
|
||||
S8* getSharedData8( S32 parentMesh, S32 size, S8 **source, bool skip );
|
||||
|
||||
/// @name Assembly Variables
|
||||
/// variables used during assembly (for skipping mesh detail levels
|
||||
/// on load and for sharing verts between meshes)
|
||||
/// @{
|
||||
|
||||
static Vector<Point3F*> smVertsList;
|
||||
static Vector<Point3F*> smNormsList;
|
||||
static Vector<U8*> smEncodedNormsList;
|
||||
|
||||
static Vector<Point2F*> smTVertsList;
|
||||
|
||||
// Optional second texture uvs.
|
||||
static Vector<Point2F*> smTVerts2List;
|
||||
|
||||
// Optional vertex colors.
|
||||
static Vector<ColorI*> smColorsList;
|
||||
|
||||
static Vector<bool> smDataCopied;
|
||||
|
||||
static const Point3F smU8ToNormalTable[];
|
||||
/// @}
|
||||
|
||||
|
||||
TSMesh();
|
||||
virtual ~TSMesh();
|
||||
|
||||
Opcode::Model *mOptTree;
|
||||
Opcode::MeshInterface* mOpMeshInterface;
|
||||
IceMaths::IndexedTriangle* mOpTris;
|
||||
IceMaths::Point* mOpPoints;
|
||||
|
||||
void prepOpcodeCollision();
|
||||
bool buildConvexOpcode( const MatrixF &mat, const Box3F &bounds, Convex *c, Convex *list );
|
||||
bool buildPolyListOpcode( const S32 od, AbstractPolyList *polyList, const Box3F &nodeBox, TSMaterialList *materials );
|
||||
bool castRayOpcode( const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials );
|
||||
|
||||
static const F32 VISIBILITY_EPSILON;
|
||||
};
|
||||
|
||||
|
||||
class TSSkinMesh : public TSMesh
|
||||
{
|
||||
public:
|
||||
struct BatchData
|
||||
{
|
||||
enum Constants
|
||||
{
|
||||
maxBonePerVert = 16, // Abitrarily chosen
|
||||
};
|
||||
|
||||
/// @name Batch by vertex
|
||||
/// These are used for batches where each element is a vertex, built by
|
||||
/// iterating over 0..maxBonePerVert bone transforms
|
||||
/// @{
|
||||
struct TransformOp
|
||||
{
|
||||
S32 transformIndex;
|
||||
F32 weight;
|
||||
|
||||
TransformOp() : transformIndex( -1 ), weight( -1.0f ) {}
|
||||
TransformOp( const S32 tIdx, const F32 w ) : transformIndex( tIdx ), weight( w ) {};
|
||||
};
|
||||
|
||||
struct BatchedVertex
|
||||
{
|
||||
S32 vertexIndex;
|
||||
S32 transformCount;
|
||||
TransformOp transform[maxBonePerVert];
|
||||
|
||||
BatchedVertex() : vertexIndex( -1 ), transformCount( -1 ) {}
|
||||
};
|
||||
|
||||
Vector<BatchedVertex> vertexBatchOperations;
|
||||
/// @}
|
||||
|
||||
/// @name Batch by Bone Transform
|
||||
/// These are used for batches where each element is a bone transform,
|
||||
/// and verts/normals are batch transformed against each element
|
||||
/// @{
|
||||
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
dALIGN(
|
||||
|
||||
struct BatchedVertWeight
|
||||
{
|
||||
Point3F vert; // Do not change the ordering of these members
|
||||
F32 weight;
|
||||
Point3F normal;
|
||||
S32 vidx;
|
||||
}
|
||||
|
||||
); // dALIGN
|
||||
|
||||
#pragma pack()
|
||||
|
||||
struct BatchedTransform
|
||||
{
|
||||
public:
|
||||
BatchedVertWeight *alignedMem;
|
||||
dsize_t numElements;
|
||||
Vector<BatchedVertWeight> *_tmpVec;
|
||||
|
||||
BatchedTransform() : alignedMem(NULL), numElements(0), _tmpVec(NULL) {}
|
||||
virtual ~BatchedTransform()
|
||||
{
|
||||
if(alignedMem)
|
||||
dFree_aligned(alignedMem);
|
||||
alignedMem = NULL;
|
||||
SAFE_DELETE(_tmpVec);
|
||||
}
|
||||
};
|
||||
SparseArray<BatchedTransform> transformBatchOperations;
|
||||
Vector<S32> transformKeys;
|
||||
/// @}
|
||||
|
||||
// # = num bones
|
||||
Vector<S32> nodeIndex;
|
||||
Vector<MatrixF> initialTransforms;
|
||||
|
||||
// # = numverts
|
||||
Vector<Point3F> initialVerts;
|
||||
Vector<Point3F> initialNorms;
|
||||
};
|
||||
|
||||
/// This method will build the batch operations and prepare the BatchData
|
||||
/// for use.
|
||||
void createBatchData();
|
||||
virtual void convertToAlignedMeshData();
|
||||
|
||||
public:
|
||||
typedef TSMesh Parent;
|
||||
|
||||
/// Structure containing data needed to batch skinning
|
||||
BatchData batchData;
|
||||
bool batchDataInitialized;
|
||||
|
||||
/// vectors that define the vertex, weight, bone tuples
|
||||
Vector<F32> weight;
|
||||
Vector<S32> boneIndex;
|
||||
Vector<S32> vertexIndex;
|
||||
|
||||
/// set verts and normals...
|
||||
void updateSkin( const Vector<MatrixF> &transforms, TSVertexBufferHandle &instanceVB, GFXPrimitiveBufferHandle &instancePB );
|
||||
|
||||
// render methods..
|
||||
void render( TSVertexBufferHandle &instanceVB, GFXPrimitiveBufferHandle &instancePB );
|
||||
void render( TSMaterialList *,
|
||||
const TSRenderState &data,
|
||||
bool isSkinDirty,
|
||||
const Vector<MatrixF> &transforms,
|
||||
TSVertexBufferHandle &vertexBuffer,
|
||||
GFXPrimitiveBufferHandle &primitiveBuffer );
|
||||
|
||||
// collision methods...
|
||||
bool buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials );
|
||||
bool castRay( S32 frame, const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials );
|
||||
bool buildConvexHull(); // does nothing, skins don't use this
|
||||
|
||||
void computeBounds( const MatrixF &transform, Box3F &bounds, S32 frame, Point3F *center, F32 *radius );
|
||||
|
||||
/// persist methods...
|
||||
void assemble( bool skip );
|
||||
void disassemble();
|
||||
|
||||
/// variables used during assembly (for skipping mesh detail levels
|
||||
/// on load and for sharing verts between meshes)
|
||||
static Vector<MatrixF*> smInitTransformList;
|
||||
static Vector<S32*> smVertexIndexList;
|
||||
static Vector<S32*> smBoneIndexList;
|
||||
static Vector<F32*> smWeightList;
|
||||
static Vector<S32*> smNodeIndexList;
|
||||
|
||||
TSSkinMesh();
|
||||
};
|
||||
|
||||
|
||||
#endif // _TSMESH_H_
|
||||
940
Engine/source/ts/tsMeshFit.cpp
Normal file
940
Engine/source/ts/tsMeshFit.cpp
Normal file
|
|
@ -0,0 +1,940 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "console/consoleTypes.h"
|
||||
#include "core/resourceManager.h"
|
||||
#include "ts/tsShapeConstruct.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
// define macros required for ConvexDecomp headers
|
||||
#if defined( _WIN32 )
|
||||
#define WIN32
|
||||
#elif defined( __MACOSX__ )
|
||||
#define APPLE
|
||||
#endif
|
||||
|
||||
#include "convexDecomp/NvFloatMath.h"
|
||||
#include "convexDecomp/NvConvexDecomposition.h"
|
||||
#include "convexDecomp/NvStanHull.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static const Point3F sFacePlanes[] = {
|
||||
Point3F( -1.0f, 0.0f, 0.0f ),
|
||||
Point3F( 1.0f, 0.0f, 0.0f ),
|
||||
Point3F( 0.0f, -1.0f, 0.0f ),
|
||||
Point3F( 0.0f, 1.0f, 0.0f ),
|
||||
Point3F( 0.0f, 0.0f, -1.0f ),
|
||||
Point3F( 0.0f, 0.0f, 1.0f ),
|
||||
};
|
||||
|
||||
static const Point3F sXEdgePlanes[] = {
|
||||
Point3F( 0.0f, -0.7071f, -0.7071f ),
|
||||
Point3F( 0.0f, -0.7071f, 0.7071f ),
|
||||
Point3F( 0.0f, 0.7071f, -0.7071f ),
|
||||
Point3F( 0.0f, 0.7071f, 0.7071f ),
|
||||
};
|
||||
|
||||
static const Point3F sYEdgePlanes[] = {
|
||||
Point3F( -0.7071f, 0.0f, -0.7071f ),
|
||||
Point3F( -0.7071f, 0.0f, 0.7071f ),
|
||||
Point3F( 0.7071f, 0.0f, -0.7071f ),
|
||||
Point3F( 0.7071f, 0.0f, 0.7071f ),
|
||||
};
|
||||
|
||||
static const Point3F sZEdgePlanes[] = {
|
||||
Point3F( -0.7071f, -0.7071f, 0.0f ),
|
||||
Point3F( -0.7071f, 0.7071f, 0.0f ),
|
||||
Point3F( 0.7071f, -0.7071f, 0.0f ),
|
||||
Point3F( 0.7071f, 0.7071f, 0.0f ),
|
||||
};
|
||||
|
||||
static const Point3F sCornerPlanes[] = {
|
||||
Point3F( -0.5774f, -0.5774f, -0.5774f ),
|
||||
Point3F( -0.5774f, -0.5774f, 0.5774f ),
|
||||
Point3F( -0.5774f, 0.5774f, -0.5774f ),
|
||||
Point3F( -0.5774f, 0.5774f, 0.5774f ),
|
||||
Point3F( 0.5774f, -0.5774f, -0.5774f ),
|
||||
Point3F( 0.5774f, -0.5774f, 0.5774f ),
|
||||
Point3F( 0.5774f, 0.5774f, -0.5774f ),
|
||||
Point3F( 0.5774f, 0.5774f, 0.5774f ),
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** A helper class for fitting primitives (Box, Sphere, Capsule) to a triangulated mesh */
|
||||
struct PrimFit
|
||||
{
|
||||
MatrixF mBoxTransform;
|
||||
Point3F mBoxSides;
|
||||
|
||||
Point3F mSphereCenter;
|
||||
F32 mSphereRadius;
|
||||
|
||||
MatrixF mCapTransform;
|
||||
F32 mCapRadius;
|
||||
F32 mCapHeight;
|
||||
|
||||
public:
|
||||
PrimFit() :
|
||||
mBoxTransform(true), mBoxSides(1,1,1),
|
||||
mSphereCenter(0,0,0), mSphereRadius(1),
|
||||
mCapTransform(true), mCapRadius(1), mCapHeight(1)
|
||||
{
|
||||
}
|
||||
|
||||
inline F32 getBoxVolume() const { return mBoxSides.x * mBoxSides.y * mBoxSides.z; }
|
||||
inline F32 getSphereVolume() const { return 4.0f / 3.0f * M_PI * mPow( mSphereRadius, 3 ); }
|
||||
inline F32 getCapsuleVolume() const { return 2 * M_PI * mPow( mCapRadius, 2 ) * (4.0f / 3.0f * mCapRadius + mCapHeight); }
|
||||
|
||||
void fitBox( U32 vertCount, const F32* verts )
|
||||
{
|
||||
CONVEX_DECOMPOSITION::fm_computeBestFitOBB( vertCount, verts, sizeof(F32)*3, (F32*)mBoxSides, (F32*)mBoxTransform );
|
||||
mBoxTransform.transpose();
|
||||
}
|
||||
|
||||
void fitSphere( U32 vertCount, const F32* verts )
|
||||
{
|
||||
mSphereRadius = CONVEX_DECOMPOSITION::fm_computeBestFitSphere( vertCount, verts, sizeof(F32)*3, (F32*)mSphereCenter );
|
||||
}
|
||||
|
||||
void fitCapsule( U32 vertCount, const F32* verts )
|
||||
{
|
||||
CONVEX_DECOMPOSITION::fm_computeBestFitCapsule( vertCount, verts, sizeof(F32)*3, mCapRadius, mCapHeight, (F32*)mCapTransform );
|
||||
mCapTransform.transpose();
|
||||
}
|
||||
};
|
||||
|
||||
class MeshFit
|
||||
{
|
||||
public:
|
||||
enum eMeshType
|
||||
{
|
||||
Box = 0,
|
||||
Sphere,
|
||||
Capsule,
|
||||
Hull,
|
||||
};
|
||||
|
||||
struct Mesh
|
||||
{
|
||||
eMeshType type;
|
||||
MatrixF transform;
|
||||
TSMesh *tsmesh;
|
||||
};
|
||||
|
||||
private:
|
||||
TSShape *mShape; ///!< Source geometry shape
|
||||
Vector<Point3F> mVerts; ///!< Source geometry verts (all meshes)
|
||||
Vector<U32> mIndices; ///!< Source geometry indices (triangle lists, all meshes)
|
||||
|
||||
bool mIsReady; ///!< Flag indicating whether we are ready to fit/create meshes
|
||||
|
||||
Vector<Mesh> mMeshes; ///!< Fitted meshes
|
||||
|
||||
void addSourceMesh( const TSShape::Object& obj, const TSMesh* mesh );
|
||||
TSMesh* initMeshFromFile( const String& filename ) const;
|
||||
TSMesh* createTriMesh( F32* verts, S32 numVerts, U32* indices, S32 numTris ) const;
|
||||
F32 maxDot( const VectorF& v ) const;
|
||||
void fitK_DOP( const Vector<Point3F>& planes );
|
||||
|
||||
public:
|
||||
MeshFit(TSShape* shape) : mShape(shape), mIsReady(false) { }
|
||||
|
||||
void setReady() { mIsReady = true; }
|
||||
bool isReady() const { return mIsReady; }
|
||||
|
||||
void initSourceGeometry( const String& target );
|
||||
|
||||
S32 getMeshCount() const { return mMeshes.size(); }
|
||||
Mesh* getMesh( S32 index ) { return &(mMeshes[index]); }
|
||||
|
||||
// Box
|
||||
void addBox( const Point3F& sides, const MatrixF& mat );
|
||||
void fitOBB();
|
||||
|
||||
// Sphere
|
||||
void addSphere( F32 radius, const Point3F& center );
|
||||
void fitSphere();
|
||||
|
||||
// Capsule
|
||||
void addCapsule( F32 radius, F32 height, const MatrixF& mat );
|
||||
void fitCapsule();
|
||||
|
||||
// k-DOP
|
||||
void fit10_DOP_X();
|
||||
void fit10_DOP_Y();
|
||||
void fit10_DOP_Z();
|
||||
void fit18_DOP();
|
||||
void fit26_DOP();
|
||||
|
||||
// Convex Hulls
|
||||
void fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThreshold, U32 maxHullVerts,
|
||||
F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError );
|
||||
};
|
||||
|
||||
|
||||
void MeshFit::initSourceGeometry( const String& target )
|
||||
{
|
||||
mMeshes.clear();
|
||||
mVerts.clear();
|
||||
mIndices.clear();
|
||||
|
||||
if ( target.equal( "bounds", String::NoCase ) )
|
||||
{
|
||||
// Add all geometry in the highest detail level
|
||||
S32 dl = 0;
|
||||
S32 ss = mShape->details[dl].subShapeNum;
|
||||
if ( ss < 0 )
|
||||
return;
|
||||
|
||||
S32 od = mShape->details[dl].objectDetailNum;
|
||||
S32 start = mShape->subShapeFirstObject[ss];
|
||||
S32 end = start + mShape->subShapeNumObjects[ss];
|
||||
|
||||
for ( S32 i = start; i < end; i++ )
|
||||
{
|
||||
const TSShape::Object &obj = mShape->objects[i];
|
||||
const TSMesh* mesh = ( od < obj.numMeshes ) ? mShape->meshes[obj.startMeshIndex + od] : NULL;
|
||||
if ( mesh )
|
||||
addSourceMesh( obj, mesh );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add highest detail mesh from this object
|
||||
S32 objIndex = mShape->findObject( target );
|
||||
if ( objIndex == -1 )
|
||||
return;
|
||||
|
||||
const TSShape::Object &obj = mShape->objects[objIndex];
|
||||
for ( S32 i = 0; i < obj.numMeshes; i++ )
|
||||
{
|
||||
const TSMesh* mesh = mShape->meshes[obj.startMeshIndex + i];
|
||||
if ( mesh )
|
||||
{
|
||||
addSourceMesh( obj, mesh );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mIsReady = ( !mVerts.empty() && !mIndices.empty() );
|
||||
}
|
||||
|
||||
void MeshFit::addSourceMesh( const TSShape::Object& obj, const TSMesh* mesh )
|
||||
{
|
||||
// Add indices
|
||||
S32 indicesBase = mIndices.size();
|
||||
for ( S32 i = 0; i < mesh->primitives.size(); i++ )
|
||||
{
|
||||
const TSDrawPrimitive& draw = mesh->primitives[i];
|
||||
if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles )
|
||||
{
|
||||
mIndices.merge( &mesh->indices[draw.start], draw.numElements );
|
||||
}
|
||||
else
|
||||
{
|
||||
U32 idx0 = mesh->indices[draw.start + 0];
|
||||
U32 idx1;
|
||||
U32 idx2 = mesh->indices[draw.start + 1];
|
||||
U32 *nextIdx = &idx1;
|
||||
for ( S32 j = 2; j < draw.numElements; j++ )
|
||||
{
|
||||
*nextIdx = idx2;
|
||||
nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
|
||||
idx2 = mesh->indices[draw.start + j];
|
||||
if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 )
|
||||
continue;
|
||||
|
||||
mIndices.push_back( idx0 );
|
||||
mIndices.push_back( idx1 );
|
||||
mIndices.push_back( idx2 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Offset indices for already added verts
|
||||
for ( S32 j = indicesBase; j < mIndices.size(); j++ )
|
||||
mIndices[j] += mVerts.size();
|
||||
|
||||
// Add verts
|
||||
S32 count, stride;
|
||||
U8* pVert;
|
||||
|
||||
if ( mesh->mVertexData.isReady() )
|
||||
{
|
||||
count = mesh->mVertexData.size();
|
||||
stride = mesh->mVertexData.vertSize();
|
||||
pVert = (U8*)mesh->mVertexData.address();
|
||||
}
|
||||
else
|
||||
{
|
||||
count = mesh->verts.size();
|
||||
stride = sizeof(Point3F);
|
||||
pVert = (U8*)mesh->verts.address();
|
||||
}
|
||||
|
||||
MatrixF objMat;
|
||||
mShape->getNodeWorldTransform( obj.nodeIndex, &objMat );
|
||||
|
||||
mVerts.reserve( mVerts.size() + count );
|
||||
for ( S32 j = 0; j < count; j++, pVert += stride )
|
||||
{
|
||||
mVerts.increment();
|
||||
objMat.mulP( *(Point3F*)pVert, &mVerts.last() );
|
||||
}
|
||||
}
|
||||
|
||||
TSMesh* MeshFit::initMeshFromFile( const String& filename ) const
|
||||
{
|
||||
// Open the source shape file and make a copy of the mesh
|
||||
Resource<TSShape> hShape = ResourceManager::get().load(filename);
|
||||
if (!bool(hShape) || !((TSShape*)hShape)->meshes.size())
|
||||
{
|
||||
Con::errorf("TSShape::createMesh: Could not load source mesh from %s", filename.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TSMesh* srcMesh = ((TSShape*)hShape)->meshes[0];
|
||||
return mShape->copyMesh( srcMesh );
|
||||
}
|
||||
|
||||
TSMesh* MeshFit::createTriMesh( F32* verts, S32 numVerts, U32* indices, S32 numTris ) const
|
||||
{
|
||||
TSMesh* mesh = mShape->copyMesh( NULL );
|
||||
mesh->numFrames = 1;
|
||||
mesh->numMatFrames = 1;
|
||||
mesh->vertsPerFrame = numVerts;
|
||||
mesh->setFlags(0);
|
||||
mesh->mHasColor = false;
|
||||
mesh->mHasTVert2 = false;
|
||||
mesh->mNumVerts = numVerts;
|
||||
|
||||
mesh->indices.reserve( numTris * 3 );
|
||||
for ( S32 i = 0; i < numTris; i++ )
|
||||
{
|
||||
mesh->indices.push_back( indices[i*3 + 0] );
|
||||
mesh->indices.push_back( indices[i*3 + 2] );
|
||||
mesh->indices.push_back( indices[i*3 + 1] );
|
||||
}
|
||||
|
||||
mesh->verts.set( verts, numVerts );
|
||||
|
||||
// Compute mesh normals
|
||||
mesh->norms.setSize( mesh->verts.size() );
|
||||
for (S32 iNorm = 0; iNorm < mesh->norms.size(); iNorm++)
|
||||
mesh->norms[iNorm] = Point3F::Zero;
|
||||
|
||||
// Sum triangle normals for each vertex
|
||||
for (S32 iInd = 0; iInd < mesh->indices.size(); iInd += 3)
|
||||
{
|
||||
// Compute the normal for this triangle
|
||||
S32 idx0 = mesh->indices[iInd + 0];
|
||||
S32 idx1 = mesh->indices[iInd + 1];
|
||||
S32 idx2 = mesh->indices[iInd + 2];
|
||||
|
||||
const Point3F& v0 = mesh->verts[idx0];
|
||||
const Point3F& v1 = mesh->verts[idx1];
|
||||
const Point3F& v2 = mesh->verts[idx2];
|
||||
|
||||
Point3F n;
|
||||
mCross(v2 - v0, v1 - v0, &n);
|
||||
n.normalize(); // remove this to use 'weighted' normals (large triangles will have more effect)
|
||||
|
||||
mesh->norms[idx0] += n;
|
||||
mesh->norms[idx1] += n;
|
||||
mesh->norms[idx2] += n;
|
||||
}
|
||||
|
||||
// Normalize the vertex normals (this takes care of averaging the triangle normals)
|
||||
for (S32 iNorm = 0; iNorm < mesh->norms.size(); iNorm++)
|
||||
mesh->norms[iNorm].normalize();
|
||||
|
||||
// Set some dummy UVs
|
||||
mesh->tverts.setSize( numVerts );
|
||||
for ( S32 j = 0; j < mesh->tverts.size(); j++ )
|
||||
mesh->tverts[j].set( 0, 0 );
|
||||
|
||||
// Add a single triangle-list primitive
|
||||
mesh->primitives.increment();
|
||||
mesh->primitives.last().start = 0;
|
||||
mesh->primitives.last().numElements = mesh->indices.size();
|
||||
mesh->primitives.last().matIndex = TSDrawPrimitive::Triangles |
|
||||
TSDrawPrimitive::Indexed |
|
||||
TSDrawPrimitive::NoMaterial;
|
||||
|
||||
mesh->createTangents( mesh->verts, mesh->norms );
|
||||
mesh->encodedNorms.set( NULL,0 );
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
F32 MeshFit::maxDot( const VectorF& v ) const
|
||||
{
|
||||
F32 maxDot = -FLT_MAX;
|
||||
for ( S32 i = 0; i < mVerts.size(); i++ )
|
||||
maxDot = getMax( maxDot, mDot( v, mVerts[i] ) );
|
||||
return maxDot;
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
// Best-fit oriented bounding box
|
||||
void MeshFit::addBox( const Point3F& sides, const MatrixF& mat )
|
||||
{
|
||||
TSMesh* mesh = initMeshFromFile( "core/art/shapes/unit_cube.dts" );
|
||||
if ( !mesh )
|
||||
return;
|
||||
|
||||
for ( S32 i = 0; i < mesh->mVertexData.size(); i++ )
|
||||
{
|
||||
Point3F v = mesh->mVertexData[i].vert();
|
||||
v.convolve( sides );
|
||||
mesh->mVertexData[i].vert( v );
|
||||
}
|
||||
mesh->computeBounds();
|
||||
|
||||
mMeshes.increment();
|
||||
mMeshes.last().type = MeshFit::Box;
|
||||
mMeshes.last().transform = mat;
|
||||
mMeshes.last().tsmesh = mesh;
|
||||
}
|
||||
|
||||
void MeshFit::fitOBB()
|
||||
{
|
||||
PrimFit primFitter;
|
||||
primFitter.fitBox( mVerts.size(), (F32*)mVerts.address() );
|
||||
addBox( primFitter.mBoxSides, primFitter.mBoxTransform );
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
// Best-fit sphere
|
||||
void MeshFit::addSphere( F32 radius, const Point3F& center )
|
||||
{
|
||||
TSMesh* mesh = initMeshFromFile( "core/art/shapes/unit_sphere.dts" );
|
||||
if ( !mesh )
|
||||
return;
|
||||
|
||||
for ( S32 i = 0; i < mesh->mVertexData.size(); i++ )
|
||||
{
|
||||
Point3F v = mesh->mVertexData[i].vert();
|
||||
mesh->mVertexData[i].vert( v * radius );
|
||||
}
|
||||
mesh->computeBounds();
|
||||
|
||||
mMeshes.increment();
|
||||
mMeshes.last().type = MeshFit::Sphere;
|
||||
mMeshes.last().transform.identity();
|
||||
mMeshes.last().transform.setPosition( center );
|
||||
mMeshes.last().tsmesh = mesh;
|
||||
}
|
||||
|
||||
void MeshFit::fitSphere()
|
||||
{
|
||||
PrimFit primFitter;
|
||||
primFitter.fitSphere( mVerts.size(), (F32*)mVerts.address() );
|
||||
addSphere( primFitter.mSphereRadius, primFitter.mSphereCenter );
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
// Best-fit capsule
|
||||
void MeshFit::addCapsule( F32 radius, F32 height, const MatrixF& mat )
|
||||
{
|
||||
TSMesh* mesh = initMeshFromFile( "core/art/shapes/unit_capsule.dts" );
|
||||
if ( !mesh )
|
||||
return;
|
||||
|
||||
// Translate and scale the mesh verts
|
||||
height = mMax( 0, height );
|
||||
F32 offset = ( height / ( 2 * radius ) ) - 0.5f;
|
||||
for ( S32 i = 0; i < mesh->mVertexData.size(); i++ )
|
||||
{
|
||||
Point3F v = mesh->mVertexData[i].vert();
|
||||
v.y += ( ( v.y > 0 ) ? offset : -offset );
|
||||
mesh->mVertexData[i].vert( v * radius );
|
||||
}
|
||||
mesh->computeBounds();
|
||||
|
||||
mMeshes.increment();
|
||||
mMeshes.last().type = MeshFit::Capsule;
|
||||
mMeshes.last().transform = mat;
|
||||
mMeshes.last().tsmesh = mesh;
|
||||
}
|
||||
|
||||
void MeshFit::fitCapsule()
|
||||
{
|
||||
PrimFit primFitter;
|
||||
primFitter.fitCapsule( mVerts.size(), (F32*)mVerts.address() );
|
||||
addCapsule( primFitter.mCapRadius, primFitter.mCapHeight, primFitter.mCapTransform );
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
// Best-fit k-discrete-oriented-polytope (where k is the number of axis-aligned planes)
|
||||
|
||||
// All faces + 4 edges (aligned to X axis) of the unit cube
|
||||
void MeshFit::fit10_DOP_X()
|
||||
{
|
||||
Vector<Point3F> planes;
|
||||
planes.setSize( 10 );
|
||||
dCopyArray( planes.address(), sFacePlanes, 6 );
|
||||
dCopyArray( planes.address()+6, sXEdgePlanes, 4 );
|
||||
fitK_DOP( planes );
|
||||
}
|
||||
|
||||
// All faces + 4 edges (aligned to Y axis) of the unit cube
|
||||
void MeshFit::fit10_DOP_Y()
|
||||
{
|
||||
Vector<Point3F> planes;
|
||||
planes.setSize( 10 );
|
||||
dCopyArray( planes.address(), sFacePlanes, 6 );
|
||||
dCopyArray( planes.address()+6, sYEdgePlanes, 4 );
|
||||
fitK_DOP( planes );
|
||||
}
|
||||
|
||||
// All faces + 4 edges (aligned to Z axis) of the unit cube
|
||||
void MeshFit::fit10_DOP_Z()
|
||||
{
|
||||
Vector<Point3F> planes;
|
||||
planes.setSize( 10 );
|
||||
dCopyArray( planes.address(), sFacePlanes, 6 );
|
||||
dCopyArray( planes.address()+6, sZEdgePlanes, 4 );
|
||||
fitK_DOP( planes );
|
||||
}
|
||||
|
||||
// All faces and edges of the unit cube
|
||||
void MeshFit::fit18_DOP()
|
||||
{
|
||||
Vector<Point3F> planes;
|
||||
planes.setSize( 18 );
|
||||
dCopyArray( planes.address(), sFacePlanes, 6 );
|
||||
dCopyArray( planes.address()+6, sXEdgePlanes, 4 );
|
||||
dCopyArray( planes.address()+10, sYEdgePlanes, 4 );
|
||||
dCopyArray( planes.address()+14, sZEdgePlanes, 4 );
|
||||
fitK_DOP( planes );
|
||||
}
|
||||
|
||||
// All faces, edges and corners of the unit cube
|
||||
void MeshFit::fit26_DOP()
|
||||
{
|
||||
Vector<Point3F> planes;
|
||||
planes.setSize( 26 );
|
||||
dCopyArray( planes.address(), sFacePlanes, 6 );
|
||||
dCopyArray( planes.address()+6, sXEdgePlanes, 4 );
|
||||
dCopyArray( planes.address()+10, sYEdgePlanes, 4 );
|
||||
dCopyArray( planes.address()+14, sZEdgePlanes, 4 );
|
||||
dCopyArray( planes.address()+18, sCornerPlanes, 8 );
|
||||
fitK_DOP( planes );
|
||||
}
|
||||
|
||||
void MeshFit::fitK_DOP( const Vector<Point3F>& planes )
|
||||
{
|
||||
// Push the planes up against the mesh
|
||||
Vector<F32> planeDs;
|
||||
for ( S32 i = 0; i < planes.size(); i++ )
|
||||
planeDs.push_back( maxDot( planes[i] ) );
|
||||
|
||||
// Collect the intersection points of any 3 planes that lie inside
|
||||
// the maximum distances found above
|
||||
Vector<Point3F> points;
|
||||
for ( S32 i = 0; i < planes.size()-2; i++ )
|
||||
{
|
||||
for ( S32 j = i+1; j < planes.size()-1; j++ )
|
||||
{
|
||||
for ( S32 k = j+1; k < planes.size(); k++ )
|
||||
{
|
||||
Point3F v23 = mCross( planes[j], planes[k] );
|
||||
F32 denom = mDot( planes[i], v23 );
|
||||
if ( denom == 0 )
|
||||
continue;
|
||||
|
||||
Point3F v31 = mCross( planes[k], planes[i] );
|
||||
Point3F v12 = mCross( planes[i], planes[j] );
|
||||
Point3F p = ( planeDs[i]*v23 + planeDs[j]*v31 + planeDs[k]*v12 ) / denom;
|
||||
|
||||
// Ignore intersection points outside the volume
|
||||
// described by the planes
|
||||
bool addPoint = true;
|
||||
for ( S32 n = 0; n < planes.size(); n++ )
|
||||
{
|
||||
if ( ( mDot( p, planes[n] ) - planeDs[n] ) > 0.005f )
|
||||
{
|
||||
addPoint = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( addPoint )
|
||||
points.push_back( p );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a convex hull from the point set
|
||||
CONVEX_DECOMPOSITION::HullDesc hd;
|
||||
hd.mVcount = points.size();
|
||||
hd.mVertices = (F32*)points.address();
|
||||
hd.mVertexStride = sizeof(Point3F);
|
||||
hd.mMaxVertices = 64;
|
||||
hd.mSkinWidth = 0.0f;
|
||||
|
||||
CONVEX_DECOMPOSITION::HullLibrary hl;
|
||||
CONVEX_DECOMPOSITION::HullResult result;
|
||||
hl.CreateConvexHull( hd, result );
|
||||
|
||||
// Create TSMesh from convex hull
|
||||
mMeshes.increment();
|
||||
mMeshes.last().type = MeshFit::Hull;
|
||||
mMeshes.last().transform.identity();
|
||||
mMeshes.last().tsmesh = createTriMesh( result.mOutputVertices, result.mNumOutputVertices,
|
||||
result.mIndices, result.mNumFaces );
|
||||
mMeshes.last().tsmesh->computeBounds();
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
// Best-fit set of convex hulls
|
||||
void MeshFit::fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThreshold, U32 maxHullVerts,
|
||||
F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError )
|
||||
{
|
||||
const F32 SkinWidth = 0.0f;
|
||||
const F32 SplitThreshold = 2.0f;
|
||||
|
||||
CONVEX_DECOMPOSITION::iConvexDecomposition *ic = CONVEX_DECOMPOSITION::createConvexDecomposition();
|
||||
|
||||
for ( S32 i = 0; i < mIndices.size(); i += 3 )
|
||||
{
|
||||
ic->addTriangle( (F32*)mVerts[mIndices[i]],
|
||||
(F32*)mVerts[mIndices[i+1]],
|
||||
(F32*)mVerts[mIndices[i+2]] );
|
||||
}
|
||||
|
||||
ic->computeConvexDecomposition(
|
||||
SkinWidth,
|
||||
depth,
|
||||
maxHullVerts,
|
||||
concavityThreshold,
|
||||
mergeThreshold,
|
||||
SplitThreshold,
|
||||
true,
|
||||
false,
|
||||
false );
|
||||
|
||||
// Add a TSMesh for each hull
|
||||
for ( S32 i = 0; i < ic->getHullCount(); i++ )
|
||||
{
|
||||
CONVEX_DECOMPOSITION::ConvexHullResult result;
|
||||
ic->getConvexHullResult( i, result );
|
||||
|
||||
eMeshType meshType = MeshFit::Hull;
|
||||
|
||||
// Check if we can use a box, sphere or capsule primitive for this hull
|
||||
if (( boxMaxError > 0 ) || ( sphereMaxError > 0 ) || ( capsuleMaxError > 0 ))
|
||||
{
|
||||
// Compute error between actual mesh and fitted primitives
|
||||
F32 meshVolume = CONVEX_DECOMPOSITION::fm_computeMeshVolume( result.mVertices, result.mTcount, result.mIndices );
|
||||
PrimFit primFitter;
|
||||
|
||||
F32 boxError = 100.0f, sphereError = 100.0f, capsuleError = 100.0f;
|
||||
if ( boxMaxError > 0 )
|
||||
{
|
||||
primFitter.fitBox( result.mVcount, result.mVertices );
|
||||
boxError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getBoxVolume() ) );
|
||||
}
|
||||
if ( sphereMaxError > 0 )
|
||||
{
|
||||
primFitter.fitSphere( result.mVcount, result.mVertices );
|
||||
sphereError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getSphereVolume() ) );
|
||||
}
|
||||
if ( capsuleMaxError > 0 )
|
||||
{
|
||||
primFitter.fitCapsule( result.mVcount, result.mVertices );
|
||||
capsuleError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getCapsuleVolume() ) );
|
||||
}
|
||||
|
||||
// Use the primitive type with smallest error less than the respective
|
||||
// max error, or Hull if none
|
||||
F32 minError = FLT_MAX;
|
||||
if ( ( boxError < boxMaxError ) && ( boxError < minError ) )
|
||||
{
|
||||
meshType = MeshFit::Box;
|
||||
minError = boxError;
|
||||
}
|
||||
if ( ( sphereError < sphereMaxError ) && ( sphereError < minError ) )
|
||||
{
|
||||
meshType = MeshFit::Sphere;
|
||||
minError = sphereError;
|
||||
}
|
||||
if ( ( capsuleError < capsuleMaxError ) && ( capsuleError < minError ) )
|
||||
{
|
||||
meshType = MeshFit::Capsule;
|
||||
minError = capsuleError;
|
||||
}
|
||||
|
||||
if ( meshType == MeshFit::Box )
|
||||
addBox( primFitter.mBoxSides, primFitter.mBoxTransform );
|
||||
else if ( meshType == MeshFit::Sphere )
|
||||
addSphere( primFitter.mSphereRadius, primFitter.mSphereCenter );
|
||||
else if ( meshType == MeshFit::Capsule )
|
||||
addCapsule( primFitter.mCapRadius, primFitter.mCapHeight, primFitter.mCapTransform );
|
||||
// else fall through to Hull processing
|
||||
}
|
||||
|
||||
if ( meshType == MeshFit::Hull )
|
||||
{
|
||||
// Create TSMesh from convex hull
|
||||
mMeshes.increment();
|
||||
mMeshes.last().type = MeshFit::Hull;
|
||||
mMeshes.last().transform.identity();
|
||||
mMeshes.last().tsmesh = createTriMesh( result.mVertices, result.mVcount, result.mIndices, result.mTcount );
|
||||
mMeshes.last().tsmesh->computeBounds();
|
||||
}
|
||||
}
|
||||
|
||||
CONVEX_DECOMPOSITION::releaseConvexDecomposition( ic );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
DefineTSShapeConstructorMethod( addPrimitive, bool, ( const char* meshName, const char* type, const char* params, TransformF txfm, const char* nodeName ),,
|
||||
( meshName, type, params, txfm, nodeName ), false,
|
||||
"Add a new mesh primitive to the shape.\n"
|
||||
"@param meshName full name (object name + detail size) of the new mesh. If "
|
||||
"no detail size is present at the end of the name, a value of 2 is used.<br>"
|
||||
"An underscore before the number at the end of the name will be interpreted as "
|
||||
"a negative sign. eg. \"MyMesh_4\" will be interpreted as \"MyMesh-4\".\n"
|
||||
"@param type one of: \"box\", \"sphere\", \"capsule\"\n"
|
||||
"@param params mesh primitive parameters:\n"
|
||||
"<ul>"
|
||||
"<li>for box: \"size_x size_y size_z\"</li>"
|
||||
"<li>for sphere: \"radius\"</li>"
|
||||
"<li>for capsule: \"height radius\"</li>"
|
||||
"</ul>"
|
||||
"</ul>\n"
|
||||
"@param txfm local transform offset from the node for this mesh\n"
|
||||
"@param nodeName name of the node to attach the new mesh to (will change the "
|
||||
"object's node if adding a new mesh to an existing object)\n"
|
||||
"@return true if successful, false otherwise\n\n"
|
||||
"@tsexample\n"
|
||||
"%this.addMesh( \"Box4\", \"box\", \"2 4 2\", \"0 2 0 0 0 1 0\", \"eye\" );\n"
|
||||
"%this.addMesh( \"Sphere256\", \"sphere\", \"2\", \"0 0 0 0 0 1 0\", \"root\" );\n"
|
||||
"%this.addMesh( \"MyCapsule-1\", \"capsule\", \"2 5\", \"0 0 2 0 0 1 0\", \"base01\" );\n"
|
||||
"@endtsexample\n" )
|
||||
{
|
||||
MeshFit fit( mShape );
|
||||
if ( !dStricmp( type, "box" ) )
|
||||
{
|
||||
// Parse box parameters
|
||||
Point3F sides;
|
||||
if ( dSscanf( params, "%g %g %g", &sides.x, &sides.y, &sides.z ) == 3 )
|
||||
{
|
||||
fit.addBox( sides, MatrixF::Identity );
|
||||
fit.setReady();
|
||||
}
|
||||
}
|
||||
else if ( !dStricmp( type, "sphere" ) )
|
||||
{
|
||||
// Parse sphere parameters
|
||||
F32 radius;
|
||||
if ( dSscanf( params, "%g", &radius ) == 1)
|
||||
{
|
||||
fit.addSphere( radius, Point3F::Zero );
|
||||
fit.setReady();
|
||||
}
|
||||
}
|
||||
else if ( !dStricmp( type, "capsule" ) )
|
||||
{
|
||||
// Parse capsule parameters
|
||||
F32 radius, height;
|
||||
if ( dSscanf( params, "%g %g", &radius, &height ) == 1)
|
||||
{
|
||||
fit.addCapsule( radius, height, MatrixF::Identity );
|
||||
fit.setReady();
|
||||
}
|
||||
}
|
||||
|
||||
if ( !fit.isReady() )
|
||||
{
|
||||
Con::errorf( "TSShapeConstructor::addPrimitive: Invalid params: '%s' for type '%s'",
|
||||
params, type );
|
||||
return false;
|
||||
}
|
||||
|
||||
TSMesh* mesh = fit.getMesh( 0 )->tsmesh;
|
||||
MatrixF mat( txfm.getMatrix() );
|
||||
|
||||
// Transform the mesh vertices
|
||||
if ( mesh->mVertexData.isReady() )
|
||||
{
|
||||
for (S32 i = 0; i < mesh->mVertexData.size(); i++)
|
||||
{
|
||||
Point3F v;
|
||||
mat.mulP( mesh->mVertexData[i].vert(), &v );
|
||||
mesh->mVertexData[i].vert( v );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (S32 i = 0; i < mesh->verts.size(); i++)
|
||||
{
|
||||
Point3F v(mesh->verts[i]);
|
||||
mat.mulP( v, &mesh->verts[i] );
|
||||
}
|
||||
}
|
||||
|
||||
// Add the mesh to the shape at the right node
|
||||
mShape->addMesh( mesh, meshName );
|
||||
|
||||
S32 dummy;
|
||||
String objName = String::GetTrailingNumber( meshName, dummy );
|
||||
setObjectNode( objName, nodeName );
|
||||
|
||||
mShape->init();
|
||||
|
||||
ADD_TO_CHANGE_SET();
|
||||
return true;
|
||||
}}
|
||||
|
||||
DefineTSShapeConstructorMethod( addCollisionDetail, bool, ( S32 size, const char* type, const char* target, S32 depth, F32 merge, F32 concavity, S32 maxVerts, F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError ), ( 4, 30, 30, 32, 0, 0, 0 ),
|
||||
( size, type, target, depth, merge, concavity, maxVerts, boxMaxError, sphereMaxError, capsuleMaxError ), false,
|
||||
"Autofit a mesh primitive or set of convex hulls to the shape geometry. Hulls "
|
||||
"may optionally be converted to boxes, spheres and/or capsules based on their "
|
||||
"volume.\n"
|
||||
"@param size size for this detail level\n"
|
||||
"@param type one of: box, sphere, capsule, 10-dop x, 10-dop y, 10-dop z, 18-dop, "
|
||||
"26-dop, convex hulls. See the Shape Editor documentation for more details "
|
||||
"about these types.\n"
|
||||
"@param target geometry to fit collision mesh(es) to; either \"bounds\" (for the "
|
||||
"whole shape), or the name of an object in the shape\n"
|
||||
"@param depth maximum split recursion depth (hulls only)\n"
|
||||
"@param merge volume % threshold used to merge hulls together (hulls only)\n"
|
||||
"@param concavity volume % threshold used to detect concavity (hulls only)\n"
|
||||
"@param maxVerts maximum number of vertices per hull (hulls only)\n"
|
||||
"@param boxMaxError max % volume difference for a hull to be converted to a "
|
||||
"box (hulls only)\n"
|
||||
"@param sphereMaxError max % volume difference for a hull to be converted to "
|
||||
"a sphere (hulls only)\n"
|
||||
"@param capsuleMaxError max % volume difference for a hull to be converted to "
|
||||
"a capsule (hulls only)\n"
|
||||
"@return true if successful, false otherwise\n\n"
|
||||
"@tsexample\n"
|
||||
"%this.addCollisionDetail( -1, \"box\", \"bounds\" );\n"
|
||||
"%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 0, 0, 0 );\n"
|
||||
"%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 50, 50, 50 );\n"
|
||||
"@endtsexample\n" )
|
||||
{
|
||||
MeshFit fit( mShape );
|
||||
fit.initSourceGeometry( target );
|
||||
if ( !fit.isReady() )
|
||||
{
|
||||
Con::errorf( "TSShapeConstructor::addCollisionDetail: Failed to initialise mesh fitter "
|
||||
"using target: %s", target );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !dStricmp( type, "box" ) )
|
||||
fit.fitOBB();
|
||||
else if ( !dStricmp( type, "sphere" ) )
|
||||
fit.fitSphere();
|
||||
else if ( !dStricmp( type, "capsule" ) )
|
||||
fit.fitCapsule();
|
||||
else if ( !dStricmp( type, "10-dop x" ) )
|
||||
fit.fit10_DOP_X();
|
||||
else if ( !dStricmp( type, "10-dop y" ) )
|
||||
fit.fit10_DOP_Y();
|
||||
else if ( !dStricmp( type, "10-dop z" ) )
|
||||
fit.fit10_DOP_Z();
|
||||
else if ( !dStricmp( type, "18-dop" ) )
|
||||
fit.fit18_DOP();
|
||||
else if ( !dStricmp( type, "26-dop" ) )
|
||||
fit.fit26_DOP();
|
||||
else if ( !dStricmp( type, "convex hulls" ) )
|
||||
{
|
||||
fit.fitConvexHulls( depth, merge, concavity, maxVerts,
|
||||
boxMaxError, sphereMaxError, capsuleMaxError );
|
||||
}
|
||||
else
|
||||
{
|
||||
Con::errorf( "TSShape::addCollisionDetail: Invalid type: '%s'", type );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now add the fitted meshes to the shape:
|
||||
// - primitives (box, sphere, capsule) need their own node (with appropriate
|
||||
// transform set) so that we can use the mesh bounds to compute the real
|
||||
// collision primitive at load time without having to examine the geometry.
|
||||
// - convex meshes may be added at the default node, with identity transform
|
||||
// - since all meshes are in the same detail level, they all get a unique
|
||||
// object name
|
||||
|
||||
const String colNodeName( String::ToString( "Col%d", size ) );
|
||||
|
||||
// Add the default node with identity transform
|
||||
S32 nodeIndex = mShape->findNode( colNodeName );
|
||||
if ( nodeIndex == -1 )
|
||||
{
|
||||
addNode( colNodeName, "" );
|
||||
}
|
||||
else
|
||||
{
|
||||
MatrixF mat;
|
||||
mShape->getNodeWorldTransform( nodeIndex, &mat );
|
||||
if ( !mat.isIdentity() )
|
||||
setNodeTransform( colNodeName, TransformF::Identity );
|
||||
}
|
||||
|
||||
// Add the meshes to the shape =>
|
||||
for ( S32 i = 0; i < fit.getMeshCount(); i++ )
|
||||
{
|
||||
MeshFit::Mesh* mesh = fit.getMesh( i );
|
||||
|
||||
// Determine a unique name for this mesh
|
||||
String objName;
|
||||
switch ( mesh->type )
|
||||
{
|
||||
case MeshFit::Box: objName = "ColBox"; break;
|
||||
case MeshFit::Sphere: objName = "ColSphere"; break;
|
||||
case MeshFit::Capsule: objName = "ColCapsule"; break;
|
||||
default: objName = "ColConvex"; break;
|
||||
}
|
||||
|
||||
for ( S32 suffix = i; suffix != 0; suffix /= 26 )
|
||||
objName += ('A' + ( suffix % 26 ) );
|
||||
String meshName = objName + String::ToString( "%d", size );
|
||||
|
||||
mShape->addMesh( mesh->tsmesh, meshName );
|
||||
|
||||
// Add a node for this object if needed (non-identity transform)
|
||||
if ( mesh->transform.isIdentity() )
|
||||
{
|
||||
mShape->setObjectNode( objName, colNodeName );
|
||||
}
|
||||
else
|
||||
{
|
||||
addNode( meshName, colNodeName, TransformF( mesh->transform ) );
|
||||
mShape->setObjectNode( objName, meshName );
|
||||
}
|
||||
}
|
||||
|
||||
mShape->init();
|
||||
|
||||
ADD_TO_CHANGE_SET();
|
||||
return true;
|
||||
}}
|
||||
121
Engine/source/ts/tsMeshIntrinsics.cpp
Normal file
121
Engine/source/ts/tsMeshIntrinsics.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "ts/tsMesh.h"
|
||||
#include "ts/tsMeshIntrinsics.h"
|
||||
#include "ts/arch/tsMeshIntrinsics.arch.h"
|
||||
#include "core/module.h"
|
||||
|
||||
|
||||
void (*zero_vert_normal_bulk)(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride) = NULL;
|
||||
void (*m_matF_x_BatchedVertWeightList)(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride) = NULL;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Default C++ Implementations (pretty slow)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void zero_vert_normal_bulk_C(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride)
|
||||
{
|
||||
register char *outData = reinterpret_cast<char *>(outPtr);
|
||||
|
||||
// TODO: Try prefetch w/ ptr de-reference
|
||||
|
||||
for(register int i = 0; i < count; i++)
|
||||
{
|
||||
TSMesh::__TSMeshVertexBase *outElem = reinterpret_cast<TSMesh::__TSMeshVertexBase *>(outData);
|
||||
outElem->_vert.zero();
|
||||
outElem->_normal.zero();
|
||||
outData += outStride;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void m_matF_x_BatchedVertWeightList_C(const MatrixF &mat,
|
||||
const dsize_t count,
|
||||
const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch,
|
||||
U8 * const __restrict outPtr,
|
||||
const dsize_t outStride)
|
||||
{
|
||||
const register MatrixF m = mat;
|
||||
|
||||
register Point3F tempPt;
|
||||
register Point3F tempNrm;
|
||||
|
||||
for(register int i = 0; i < count; i++)
|
||||
{
|
||||
const TSSkinMesh::BatchData::BatchedVertWeight &inElem = batch[i];
|
||||
|
||||
TSMesh::__TSMeshVertexBase *outElem = reinterpret_cast<TSMesh::__TSMeshVertexBase *>(outPtr + inElem.vidx * outStride);
|
||||
|
||||
m.mulP( inElem.vert, &tempPt );
|
||||
m.mulV( inElem.normal, &tempNrm );
|
||||
|
||||
outElem->_vert += ( tempPt * inElem.weight );
|
||||
outElem->_normal += ( tempNrm * inElem.weight );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Initializer.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
MODULE_BEGIN( TSMeshIntrinsics )
|
||||
|
||||
MODULE_INIT_AFTER( 3D )
|
||||
|
||||
MODULE_INIT
|
||||
{
|
||||
// Assign defaults (C++ versions)
|
||||
zero_vert_normal_bulk = zero_vert_normal_bulk_C;
|
||||
m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_C;
|
||||
|
||||
#if defined(TORQUE_OS_XENON)
|
||||
zero_vert_normal_bulk = zero_vert_normal_bulk_X360;
|
||||
m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_X360;
|
||||
#else
|
||||
// Find the best implementation for the current CPU
|
||||
if(Platform::SystemInfo.processor.properties & CPU_PROP_SSE)
|
||||
{
|
||||
#if defined(TORQUE_CPU_X86)
|
||||
|
||||
zero_vert_normal_bulk = zero_vert_normal_bulk_SSE;
|
||||
m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_SSE;
|
||||
|
||||
/* This code still has a bug left in it
|
||||
#if (_MSC_VER >= 1500)
|
||||
if(Platform::SystemInfo.processor.properties & CPU_PROP_SSE4_1)
|
||||
m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_SSE4;
|
||||
#endif
|
||||
*/
|
||||
#endif
|
||||
}
|
||||
else if(Platform::SystemInfo.processor.properties & CPU_PROP_ALTIVEC)
|
||||
{
|
||||
#if !defined(TORQUE_OS_XENON) && defined(TORQUE_CPU_PPC)
|
||||
zero_vert_normal_bulk = zero_vert_normal_bulk_gccvec;
|
||||
m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_gccvec;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MODULE_END;
|
||||
51
Engine/source/ts/tsMeshIntrinsics.h
Normal file
51
Engine/source/ts/tsMeshIntrinsics.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSMESHINTRINSICS_H_
|
||||
#define _TSMESHINTRINSICS_H_
|
||||
|
||||
/// This is the batch-by-transform skin loop
|
||||
///
|
||||
/// @param mat Bone transform
|
||||
/// @param count Number of input elements in the batch
|
||||
/// @param batch Pointer to the first element in an aligned array of input elements
|
||||
/// @param outPtr Pointer to index 0 of a TSMesh aligned vertex buffer
|
||||
/// @param outStride Size, in bytes, of one entry in the vertex buffer
|
||||
extern void (*m_matF_x_BatchedVertWeightList)
|
||||
(const MatrixF &mat,
|
||||
const dsize_t count,
|
||||
const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch,
|
||||
U8 * const __restrict outPtr,
|
||||
const dsize_t outStride);
|
||||
|
||||
/// Set the vertex position and normal to (0, 0, 0)
|
||||
///
|
||||
/// @param count Number of elements
|
||||
/// @param outPtr Pointer to a TSMesh aligned vertex buffer
|
||||
/// @param outStride Size, in bytes, of one entry in the vertex buffer
|
||||
extern void (*zero_vert_normal_bulk)
|
||||
(const dsize_t count,
|
||||
U8 * __restrict const outPtr,
|
||||
const dsize_t outStride);
|
||||
|
||||
#endif
|
||||
|
||||
371
Engine/source/ts/tsPartInstance.cpp
Normal file
371
Engine/source/ts/tsPartInstance.cpp
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "ts/tsPartInstance.h"
|
||||
#include "math/mMath.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Constructors
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
MRandomR250 TSPartInstance::smRandom;
|
||||
|
||||
TSPartInstance::TSPartInstance(TSShapeInstance * sourceShape)
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mMeshObjects);
|
||||
|
||||
init(sourceShape);
|
||||
}
|
||||
|
||||
TSPartInstance::TSPartInstance(TSShapeInstance * sourceShape, S32 objectIndex)
|
||||
{
|
||||
init(sourceShape);
|
||||
addObject(objectIndex);
|
||||
}
|
||||
|
||||
void TSPartInstance::init(TSShapeInstance * sourceShape)
|
||||
{
|
||||
mSourceShape = sourceShape;
|
||||
mSizeCutoffs = NULL;
|
||||
mPolyCount = NULL;
|
||||
mNumDetails = 0;
|
||||
mCurrentObjectDetail = 0;
|
||||
mCurrentIntraDL = 1.0f;
|
||||
mData = 0;
|
||||
}
|
||||
|
||||
TSPartInstance::~TSPartInstance()
|
||||
{
|
||||
delete [] mPolyCount;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Methods for updating PartInstances
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void TSPartInstance::addObject(S32 objectIndex)
|
||||
{
|
||||
if (mSourceShape->mMeshObjects[objectIndex].forceHidden ||
|
||||
mSourceShape->mMeshObjects[objectIndex].visible < 0.01f)
|
||||
// not visible, don't bother
|
||||
return;
|
||||
|
||||
mMeshObjects.push_back(&mSourceShape->mMeshObjects[objectIndex]);
|
||||
}
|
||||
|
||||
void TSPartInstance::updateBounds()
|
||||
{
|
||||
// run through meshes and brute force it?
|
||||
Box3F bounds;
|
||||
mBounds.minExtents.set( 10E30f, 10E30f, 10E30f);
|
||||
mBounds.maxExtents.set(-10E30f,-10E30f,-10E30f);
|
||||
for (S32 i=0; i<mMeshObjects.size(); i++)
|
||||
{
|
||||
if (mMeshObjects[i]->getMesh(0))
|
||||
mMeshObjects[i]->getMesh(0)->computeBounds(mMeshObjects[i]->getTransform(),bounds,mMeshObjects[i]->frame);
|
||||
mBounds.minExtents.setMin(bounds.minExtents);
|
||||
mBounds.maxExtents.setMax(bounds.maxExtents);
|
||||
}
|
||||
mCenter = mBounds.minExtents + mBounds.maxExtents;
|
||||
mCenter *= 0.5f;
|
||||
Point3F r = mBounds.maxExtents-mCenter;
|
||||
mRadius = mSqrt(mDot(r,r));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Methods for breaking shapes into pieces
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void TSPartInstance::breakShape(TSShapeInstance * shape, S32 subShape, Vector<TSPartInstance*> & partList, F32 * probShatter, F32 * probBreak, S32 probDepth)
|
||||
{
|
||||
AssertFatal(subShape>=0 && subShape<shape->mShape->subShapeFirstNode.size(),"TSPartInstance::breakShape: subShape out of range.");
|
||||
|
||||
S32 start = shape->mShape->subShapeFirstNode[subShape];
|
||||
|
||||
TSPartInstance::breakShape(shape, NULL, start, partList, probShatter, probBreak, probDepth);
|
||||
|
||||
// update bounds (and get rid of empty parts)
|
||||
for (S32 i=0; i<partList.size(); i++)
|
||||
{
|
||||
if (partList[i]->mMeshObjects.size())
|
||||
partList[i]->updateBounds();
|
||||
else
|
||||
{
|
||||
partList.erase(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TSPartInstance::breakShape(TSShapeInstance * shape, TSPartInstance * currentPart, S32 currentNode, Vector<TSPartInstance*> & partList, F32 * probShatter, F32 * probBreak, S32 probDepth)
|
||||
{
|
||||
AssertFatal( !probDepth || (probShatter && probBreak),"TSPartInstance::breakShape: probabilities improperly specified.");
|
||||
|
||||
const TSShape::Node * node = &shape->mShape->nodes[currentNode];
|
||||
S32 object = node->firstObject;
|
||||
S32 child = node->firstChild;
|
||||
|
||||
// copy off probabilities and update probability lists for next level
|
||||
F32 ps = probShatter ? *probShatter : 1.0f;
|
||||
F32 pb = probBreak ? *probBreak : 1.0f;
|
||||
if (probDepth>1 && probShatter && probBreak)
|
||||
{
|
||||
probShatter++;
|
||||
probBreak++;
|
||||
probDepth--;
|
||||
}
|
||||
|
||||
// what to do...depending on how the die roll, we can:
|
||||
// a) shatter the shape at this level -- meaning we make a part out of each object on this node and
|
||||
// we make parts out of all the children (perhaps breaking them up further still)
|
||||
// b) break the shape off at this level -- meaning we make a part out of the intact piece from here
|
||||
// on down (again, we might break the result further as we iterate through the nodes...what breaking
|
||||
// the shape really does is separate this piece from the parent piece).
|
||||
// c) add this piece to the parent -- meaning all objects on this node are added to the parent, and children
|
||||
// are also added (but children will be recursively sent through this routine, so if a parent gets option
|
||||
// (c) and the child option (a) or (b), then the child will be ripped from the parents grasp. Cruel
|
||||
// people us coders are.
|
||||
// Note: (a) is the only way that two objects on the same node can be separated...that is why both
|
||||
// option a and option b are needed.
|
||||
if (!probShatter || smRandom.randF() < ps)
|
||||
{
|
||||
// option a -- shatter the shape at this level
|
||||
|
||||
// iterate through the objects, make part out of each one
|
||||
while (object>=0)
|
||||
{
|
||||
partList.increment();
|
||||
partList.last() = new TSPartInstance(shape,object);
|
||||
object = shape->mShape->objects[object].nextSibling;
|
||||
}
|
||||
|
||||
// iterate through the child nodes, call ourselves on each one with currentPart = NULL
|
||||
while (child>=0)
|
||||
{
|
||||
TSPartInstance::breakShape(shape,NULL,child,partList,probShatter,probBreak,probDepth);
|
||||
child = shape->mShape->nodes[child].nextSibling;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!probBreak || smRandom.randF() < pb)
|
||||
// option b -- break the shape off at this level
|
||||
currentPart = NULL; // fall through to option C
|
||||
|
||||
// option c -- add this piece to the parent
|
||||
|
||||
if (!currentPart)
|
||||
{
|
||||
currentPart = new TSPartInstance(shape);
|
||||
partList.push_back(currentPart);
|
||||
}
|
||||
|
||||
// iterate through objects, add to currentPart
|
||||
while (object>=0)
|
||||
{
|
||||
currentPart->addObject(object);
|
||||
object = shape->mShape->objects[object].nextSibling;
|
||||
}
|
||||
|
||||
// iterate through child nodes, call ourselves on each one with currentPart as is
|
||||
while (child>=0)
|
||||
{
|
||||
TSPartInstance::breakShape(shape,currentPart,child,partList,probShatter,probBreak,probDepth);
|
||||
child = shape->mShape->nodes[child].nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// render methods -- we use TSShapeInstance code as much as possible
|
||||
// issues: setupTexturing expects a detail level, we give it an object detail level
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void TSPartInstance::render(S32 od, const TSRenderState &rdata)
|
||||
{
|
||||
S32 i;
|
||||
|
||||
// render mesh objects
|
||||
for (i=0; i<mMeshObjects.size(); i++)
|
||||
mMeshObjects[i]->render(od,mSourceShape->getMaterialList(),rdata,1.0);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Detail selection
|
||||
// 2 methods:
|
||||
// method 1: use source shapes detail levels...
|
||||
// method 2: pass in our own table...
|
||||
// In either case, you can compute the pixel size on your own or let open gl do it.
|
||||
// If you want to use method 2, you have to call setDetailData sometime before selecting detail
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void TSPartInstance::setDetailData(F32 * sizeCutoffs, S32 numDetails)
|
||||
{
|
||||
if (mSizeCutoffs == sizeCutoffs && mNumDetails==numDetails)
|
||||
return;
|
||||
|
||||
mSizeCutoffs = sizeCutoffs;
|
||||
mNumDetails = numDetails;
|
||||
delete [] mPolyCount;
|
||||
mPolyCount = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
void TSPartInstance::selectCurrentDetail(bool ignoreScale)
|
||||
{
|
||||
if (mSizeCutoffs)
|
||||
{
|
||||
selectCurrentDetail(mSizeCutoffs,mNumDetails,ignoreScale);
|
||||
return;
|
||||
}
|
||||
|
||||
mSourceShape->selectCurrentDetail(ignoreScale);
|
||||
mCurrentObjectDetail = mSourceShape->getCurrentDetail();
|
||||
mCurrentIntraDL = mSourceShape->getCurrentIntraDetail();
|
||||
}
|
||||
|
||||
void TSPartInstance::selectCurrentDetail(F32 pixelSize)
|
||||
{
|
||||
if (mSizeCutoffs)
|
||||
{
|
||||
selectCurrentDetail(pixelSize,mSizeCutoffs,mNumDetails);
|
||||
return;
|
||||
}
|
||||
|
||||
mSourceShape->selectCurrentDetail(pixelSize);
|
||||
mCurrentObjectDetail = mSourceShape->getCurrentDetail();
|
||||
mCurrentIntraDL = mSourceShape->getCurrentIntraDetail();
|
||||
}
|
||||
|
||||
void TSPartInstance::selectCurrentDetail(F32 dist, F32 invScale)
|
||||
{
|
||||
if (mSizeCutoffs)
|
||||
{
|
||||
const RectI &viewport = GFX->getViewport();
|
||||
F32 pixelScale = viewport.extent.x * 1.6f / 640.0f;
|
||||
F32 pixelSize = GFX->projectRadius(dist*invScale,mSourceShape->getShape()->radius) * pixelScale * TSShapeInstance::smDetailAdjust;
|
||||
selectCurrentDetail(pixelSize,mSizeCutoffs,mNumDetails);
|
||||
return;
|
||||
}
|
||||
|
||||
mSourceShape->selectCurrentDetail(dist, invScale);
|
||||
mCurrentObjectDetail = mSourceShape->getCurrentDetail();
|
||||
mCurrentIntraDL = mSourceShape->getCurrentIntraDetail();
|
||||
}
|
||||
|
||||
void TSPartInstance::selectCurrentDetail(F32 * sizeCutoffs, S32 numDetails, bool ignoreScale)
|
||||
{
|
||||
// compute pixel size
|
||||
Point3F p;
|
||||
MatrixF toCam = GFX->getWorldMatrix();
|
||||
toCam.mulP(mCenter,&p);
|
||||
F32 dist = mDot(p,p);
|
||||
F32 scale = 1.0f;
|
||||
if (!ignoreScale)
|
||||
{
|
||||
// any scale?
|
||||
Point3F x,y,z;
|
||||
toCam.getRow(0,&x);
|
||||
toCam.getRow(1,&y);
|
||||
toCam.getRow(2,&z);
|
||||
F32 scalex = mDot(x,x);
|
||||
F32 scaley = mDot(y,y);
|
||||
F32 scalez = mDot(z,z);
|
||||
scale = scalex;
|
||||
if (scaley > scale)
|
||||
scale = scaley;
|
||||
if (scalez > scale)
|
||||
scale = scalez;
|
||||
}
|
||||
dist /= scale;
|
||||
dist = mSqrt(dist);
|
||||
|
||||
const RectI &viewport = GFX->getViewport();
|
||||
// JMQMERGE: is this using a hardcoded res/aspect ? (and the code above)
|
||||
F32 pixelScale = viewport.extent.x * 1.6f / 640.0f;
|
||||
F32 pixelRadius = GFX->projectRadius(dist,mRadius) * pixelScale * TSShapeInstance::smDetailAdjust;
|
||||
|
||||
selectCurrentDetail(pixelRadius,sizeCutoffs,numDetails);
|
||||
}
|
||||
|
||||
void TSPartInstance::selectCurrentDetail(F32 pixelSize, F32 * sizeCutoffs, S32 numDetails)
|
||||
{
|
||||
mCurrentObjectDetail = 0;
|
||||
while (numDetails)
|
||||
{
|
||||
if (pixelSize > *sizeCutoffs)
|
||||
return;
|
||||
mCurrentObjectDetail++;
|
||||
numDetails--;
|
||||
sizeCutoffs++;
|
||||
}
|
||||
mCurrentObjectDetail = -1;
|
||||
}
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Detail query methods...complicated because there are two ways that detail information
|
||||
// can be determined...1) using source shape, or 2) using mSizeCutoffs
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
F32 TSPartInstance::getDetailSize(S32 dl) const
|
||||
{
|
||||
if (dl<0)
|
||||
return 0;
|
||||
else if (mSizeCutoffs && dl<mNumDetails)
|
||||
return mSizeCutoffs[dl];
|
||||
else if (!mSizeCutoffs && dl<=mSourceShape->getShape()->mSmallestVisibleDL)
|
||||
return mSourceShape->getShape()->details[dl].size;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
S32 TSPartInstance::getPolyCount(S32 dl)
|
||||
{
|
||||
if (!mPolyCount)
|
||||
computePolyCount();
|
||||
|
||||
if (dl<0 || dl>=mNumDetails)
|
||||
return 0;
|
||||
else
|
||||
return mPolyCount[dl];
|
||||
}
|
||||
|
||||
void TSPartInstance::computePolyCount()
|
||||
{
|
||||
if (!mSizeCutoffs)
|
||||
mNumDetails = mSourceShape->getShape()->mSmallestVisibleDL+1;
|
||||
|
||||
delete [] mPolyCount;
|
||||
mPolyCount = new S32[mNumDetails];
|
||||
|
||||
for (S32 i=0; i<mNumDetails; i++)
|
||||
{
|
||||
mPolyCount[i] = 0;
|
||||
for (S32 j=0; j<mMeshObjects.size(); j++)
|
||||
{
|
||||
if (mMeshObjects[j]->getMesh(i))
|
||||
mPolyCount[i] += mMeshObjects[j]->getMesh(i)->getNumPolys();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
138
Engine/source/ts/tsPartInstance.h
Normal file
138
Engine/source/ts/tsPartInstance.h
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSPARTINSTANCE_H_
|
||||
#define _TSPARTINSTANCE_H_
|
||||
|
||||
#ifndef _TSSHAPEINSTANCE_H_
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#endif
|
||||
|
||||
class TSPartInstance
|
||||
{
|
||||
/// TSPartInstance assumes ownership (or shared ownership) of the source shape. This means that the source
|
||||
/// shape cannot be deleted so long as the part instance is still around. This also means that any change
|
||||
/// to source shapes's transforms or other animation properties will affect how the part instance displays.
|
||||
/// It is ok (even expected), however, to have many part instances accessing the same shape.
|
||||
TSShapeInstance * mSourceShape;
|
||||
|
||||
/// @name Bounding info
|
||||
/// @{
|
||||
|
||||
Box3F mBounds;
|
||||
Point3F mCenter;
|
||||
F32 mRadius;
|
||||
/// @}
|
||||
|
||||
/// detail selection uses the table pointed to by this member
|
||||
///
|
||||
/// if this member is blank, then it uses source shape to determine detail...
|
||||
///
|
||||
/// detail 0 draws up until size of shape is less than mSizeCutoffs[0], detail 1 until mSizeCutoffs[1], etc.
|
||||
F32 * mSizeCutoffs;
|
||||
S32 * mPolyCount;
|
||||
S32 mNumDetails;
|
||||
|
||||
/// @name Detail Levels
|
||||
/// detail levels on part instance correspond directly
|
||||
/// to object details on objects -- this is different
|
||||
/// from shape instances where dl corresponds to a
|
||||
/// subtree number and object detail. The reason
|
||||
/// for this is that partinstances are derived from
|
||||
/// a single subtree of a shape instance, so the subtree
|
||||
/// is implied (implied by which objects are in the part instance)...
|
||||
/// @{
|
||||
S32 mCurrentObjectDetail;
|
||||
F32 mCurrentIntraDL;
|
||||
/// @}
|
||||
|
||||
Vector<TSShapeInstance::MeshObjectInstance*> mMeshObjects;
|
||||
|
||||
static MRandomR250 smRandom;
|
||||
|
||||
void addObject(S32 objectIndex);
|
||||
void updateBounds();
|
||||
|
||||
void renderDetailMap(S32 od);
|
||||
void renderEnvironmentMap(S32 od);
|
||||
void renderFog(S32 od);
|
||||
|
||||
void init(TSShapeInstance *);
|
||||
|
||||
static void breakShape(TSShapeInstance *, TSPartInstance *, S32 currentNode,
|
||||
Vector<TSPartInstance*> & partList, F32 * probShatter,
|
||||
F32 * probBreak, S32 probDepth);
|
||||
|
||||
/// @name Private Detail Selection Methods
|
||||
/// @{
|
||||
void selectCurrentDetail(F32 * sizeCutoffs, S32 numDetails, bool ignoreScale);
|
||||
void selectCurrentDetail(F32 pixelSize, F32 * sizeCutoffs, S32 numDetails);
|
||||
void computePolyCount();
|
||||
/// @}
|
||||
|
||||
public:
|
||||
|
||||
TSPartInstance(TSShapeInstance * source);
|
||||
TSPartInstance(TSShapeInstance * source, S32 objectIndex);
|
||||
~TSPartInstance();
|
||||
|
||||
const TSShape * getShape() { return mSourceShape->getShape(); }
|
||||
TSShapeInstance * getSourceShapeInstance(){ return mSourceShape; }
|
||||
|
||||
static void breakShape(TSShapeInstance *, S32 subShape, Vector<TSPartInstance*> & partList, F32 * probShatter, F32 * probBreak, S32 probDepth);
|
||||
|
||||
Point3F & getCenter() { return mCenter; }
|
||||
Box3F & getBounds() { return mBounds; }
|
||||
F32 & getRadius() { return mRadius; }
|
||||
|
||||
void render( const TSRenderState &rdata ) { render( mCurrentObjectDetail, rdata ); }
|
||||
void render( S32 dl, const TSRenderState &rdata );
|
||||
|
||||
/// choose detail method -- pass in NULL for first parameter to just use shapes data
|
||||
void setDetailData(F32 * sizeCutoffs, S32 numDetails);
|
||||
|
||||
/// @name Detail Selection
|
||||
/// @{
|
||||
/*
|
||||
void selectCurrentDetail(bool ignoreScale = false);
|
||||
void selectCurrentDetail(F32 pixelSize);
|
||||
void selectCurrentDetail2(F32 adjustedDist);
|
||||
*/
|
||||
/// @}
|
||||
|
||||
/// @name Detail Information Accessors
|
||||
/// @{
|
||||
F32 getDetailSize( S32 dl ) const;
|
||||
S32 getPolyCount( S32 dl );
|
||||
S32 getNumDetails() const { return mSizeCutoffs ? mNumDetails : mSourceShape->getShape()->mSmallestVisibleDL+1; }
|
||||
|
||||
S32 getCurrentObjectDetail() const { return mCurrentObjectDetail; }
|
||||
void setCurrentObjectDetail(S32 od) { mCurrentObjectDetail = od; }
|
||||
F32 getCurrentIntraDetail() const { return mCurrentIntraDL; }
|
||||
void setCurrentIntraDetail(F32 intra) { mCurrentIntraDL = intra; }
|
||||
/// @}
|
||||
|
||||
void *mData; ///< for use by app
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
51
Engine/source/ts/tsRenderState.cpp
Normal file
51
Engine/source/ts/tsRenderState.cpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/tsRenderState.h"
|
||||
|
||||
|
||||
TSRenderState::TSRenderState()
|
||||
: mState( NULL ),
|
||||
mCubemap( NULL ),
|
||||
mFadeOverride( 1.0f ),
|
||||
mNoRenderTranslucent( false ),
|
||||
mNoRenderNonTranslucent( false ),
|
||||
mMaterialHint( NULL ),
|
||||
mCuller( NULL ),
|
||||
mLightQuery( NULL ),
|
||||
mUseOriginSort( false )
|
||||
{
|
||||
}
|
||||
|
||||
TSRenderState::TSRenderState( const TSRenderState &state )
|
||||
: mState( state.mState ),
|
||||
mCubemap( state.mCubemap ),
|
||||
mFadeOverride( state.mFadeOverride ),
|
||||
mNoRenderTranslucent( state.mNoRenderTranslucent ),
|
||||
mNoRenderNonTranslucent( state.mNoRenderNonTranslucent ),
|
||||
mMaterialHint( state.mMaterialHint ),
|
||||
mCuller( state.mCuller ),
|
||||
mLightQuery( state.mLightQuery ),
|
||||
mUseOriginSort( state.mUseOriginSort )
|
||||
{
|
||||
}
|
||||
153
Engine/source/ts/tsRenderState.h
Normal file
153
Engine/source/ts/tsRenderState.h
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSRENDERDATA_H_
|
||||
#define _TSRENDERDATA_H_
|
||||
|
||||
#ifndef _MMATRIX_H_
|
||||
#include "math/mMatrix.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
class SceneRenderState;
|
||||
class GFXCubemap;
|
||||
class Frustum;
|
||||
class LightQuery;
|
||||
|
||||
|
||||
/// A simple class for passing render state through the pre-render pipeline.
|
||||
///
|
||||
/// @section TSRenderState_intro Introduction
|
||||
///
|
||||
/// TSRenderState holds on to certain pieces of data that may be
|
||||
/// set at the preparation stage of rendering (prepRengerImage etc.)
|
||||
/// which are needed further along in the process of submitting
|
||||
/// a render instance for later rendering by the RenderManager.
|
||||
///
|
||||
/// It was created to clean up and refactor the DTS rendering
|
||||
/// from having a large number of static data that would be used
|
||||
/// in varying places. These statics were confusing and would often
|
||||
/// cause problems when not properly cleaned up by various objects after
|
||||
/// submitting their RenderInstances.
|
||||
///
|
||||
/// @section TSRenderState_functionality What Does TSRenderState Do?
|
||||
///
|
||||
/// TSRenderState is a simple class that performs the function of passing along
|
||||
/// (from the prep function(s) to the actual submission) the data
|
||||
/// needed for the desired state of rendering.
|
||||
///
|
||||
/// @section TSRenderState_example Usage Example
|
||||
///
|
||||
/// TSRenderState is very easy to use. Merely create a TSRenderState object (in prepRenderImage usually)
|
||||
/// and set any of the desired data members (SceneRenderState, camera transform etc.), and pass the address of
|
||||
/// your TSRenderState to your render function.
|
||||
///
|
||||
class TSRenderState
|
||||
{
|
||||
protected:
|
||||
|
||||
const SceneRenderState *mState;
|
||||
|
||||
GFXCubemap *mCubemap;
|
||||
|
||||
/// Used to override the normal
|
||||
/// fade value of an object.
|
||||
/// This is multiplied by the current
|
||||
/// fade value of the instance
|
||||
/// to gain the resulting visibility fade (see TSMesh::render()).
|
||||
F32 mFadeOverride;
|
||||
|
||||
/// These are used in some places
|
||||
/// TSShapeInstance::render, however,
|
||||
/// it appears they are never set to anything
|
||||
/// other than false. We provide methods
|
||||
/// for setting them regardless.
|
||||
bool mNoRenderTranslucent;
|
||||
bool mNoRenderNonTranslucent;
|
||||
|
||||
/// A generic hint value passed from the game
|
||||
/// code down to the material for use by shader
|
||||
/// features.
|
||||
void *mMaterialHint;
|
||||
|
||||
/// An optional object space frustum used to cull
|
||||
/// subobjects within the shape.
|
||||
const Frustum *mCuller;
|
||||
|
||||
/// Use the origin point of the mesh for distance
|
||||
/// sorting for transparency instead of the nearest
|
||||
/// bounding box point.
|
||||
bool mUseOriginSort;
|
||||
|
||||
/// The lighting query object used if any materials
|
||||
/// are forward lit and need lights.
|
||||
LightQuery *mLightQuery;
|
||||
|
||||
public:
|
||||
|
||||
TSRenderState();
|
||||
TSRenderState( const TSRenderState &state );
|
||||
|
||||
/// @name Get/Set methods.
|
||||
/// @{
|
||||
|
||||
///@see mState
|
||||
const SceneRenderState* getSceneState() const { return mState; }
|
||||
void setSceneState( const SceneRenderState *state ) { mState = state; }
|
||||
|
||||
///@see mCubemap
|
||||
GFXCubemap* getCubemap() const { return mCubemap; }
|
||||
void setCubemap( GFXCubemap *cubemap ) { mCubemap = cubemap; }
|
||||
|
||||
///@see mFadeOverride
|
||||
F32 getFadeOverride() const { return mFadeOverride; }
|
||||
void setFadeOverride( F32 fade ) { mFadeOverride = fade; }
|
||||
|
||||
///@see mNoRenderTranslucent
|
||||
bool isNoRenderTranslucent() const { return mNoRenderTranslucent; }
|
||||
void setNoRenderTranslucent( bool noRenderTrans ) { mNoRenderTranslucent = noRenderTrans; }
|
||||
|
||||
///@see mNoRenderNonTranslucent
|
||||
bool isNoRenderNonTranslucent() const { return mNoRenderNonTranslucent; }
|
||||
void setNoRenderNonTranslucent( bool noRenderNonTrans ) { mNoRenderNonTranslucent = noRenderNonTrans; }
|
||||
|
||||
///@see mMaterialHint
|
||||
void* getMaterialHint() const { return mMaterialHint; }
|
||||
void setMaterialHint( void *materialHint ) { mMaterialHint = materialHint; }
|
||||
|
||||
///@see mCuller
|
||||
const Frustum* getCuller() const { return mCuller; }
|
||||
void setCuller( const Frustum *culler ) { mCuller = culler; }
|
||||
|
||||
///@see mUseOriginSort
|
||||
void setOriginSort( bool enable ) { mUseOriginSort = enable; }
|
||||
bool useOriginSort() const { return mUseOriginSort; }
|
||||
|
||||
///@see mLightQuery
|
||||
void setLightQuery( LightQuery *query ) { mLightQuery = query; }
|
||||
LightQuery* getLightQuery() const { return mLightQuery; }
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
#endif // _TSRENDERDATA_H_
|
||||
2289
Engine/source/ts/tsShape.cpp
Normal file
2289
Engine/source/ts/tsShape.cpp
Normal file
File diff suppressed because it is too large
Load diff
692
Engine/source/ts/tsShape.h
Normal file
692
Engine/source/ts/tsShape.h
Normal file
|
|
@ -0,0 +1,692 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSSHAPE_H_
|
||||
#define _TSSHAPE_H_
|
||||
|
||||
#ifndef _TSMESH_H_
|
||||
#include "ts/tsMesh.h"
|
||||
#endif
|
||||
#ifndef _TSINTEGERSET_H_
|
||||
#include "ts/tsIntegerSet.h"
|
||||
#endif
|
||||
#ifndef _TSTRANSFORM_H_
|
||||
#include "ts/tsTransform.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPEALLOC_H_
|
||||
#include "ts/tsShapeAlloc.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define DTS_EXPORTER_CURRENT_VERSION 124
|
||||
|
||||
class TSMaterialList;
|
||||
class TSLastDetail;
|
||||
class PhysicsCollision;
|
||||
|
||||
//
|
||||
struct CollisionShapeInfo
|
||||
{
|
||||
S32 colNode;
|
||||
PhysicsCollision *colShape;
|
||||
};
|
||||
|
||||
/// TSShape stores generic data for a 3space model.
|
||||
///
|
||||
/// TSShape and TSShapeInstance act in conjunction to allow the rendering and
|
||||
/// manipulation of a three dimensional model.
|
||||
///
|
||||
/// @note The material lists are the only thing that is not loaded in TSShape.
|
||||
/// instead, they are loaded in TSShapeInstance because of a former restriction
|
||||
/// on the resource manager where only one file could be opened at a time.
|
||||
/// The resource manager restriction has been resolved, but the material
|
||||
/// lists are still loaded in TSShapeInstance.
|
||||
///
|
||||
/// @see TSShapeInstance for a further discussion of the 3space system.
|
||||
class TSShape
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
UniformScale = BIT(0),
|
||||
AlignedScale = BIT(1),
|
||||
ArbitraryScale = BIT(2),
|
||||
Blend = BIT(3),
|
||||
Cyclic = BIT(4),
|
||||
MakePath = BIT(5),
|
||||
HasTranslucency= BIT(6),
|
||||
AnyScale = UniformScale | AlignedScale | ArbitraryScale
|
||||
};
|
||||
|
||||
/// Nodes hold the transforms in the shape's tree. They are the bones of the skeleton.
|
||||
struct Node
|
||||
{
|
||||
S32 nameIndex;
|
||||
S32 parentIndex;
|
||||
|
||||
// computed at runtime
|
||||
S32 firstObject;
|
||||
S32 firstChild;
|
||||
S32 nextSibling;
|
||||
};
|
||||
|
||||
/// Objects hold renderable items (in particular meshes).
|
||||
///
|
||||
/// Each object has a number of meshes associated with it.
|
||||
/// Each mesh corresponds to a different detail level.
|
||||
///
|
||||
/// meshIndicesIndex points to numMeshes consecutive indices
|
||||
/// into the meshList and meshType vectors. It indexes the
|
||||
/// meshIndexList vector (meshIndexList is merely a clearinghouse
|
||||
/// for the object's mesh lists). Some indices may correspond to
|
||||
/// no mesh -- which means no mesh will be drawn for the part for
|
||||
/// the given detail level. See comments on the meshIndexList
|
||||
/// for how null meshes are coded.
|
||||
///
|
||||
/// @note Things are stored this way so that there are no pointers.
|
||||
/// This makes serialization to disk dramatically simpler.
|
||||
struct Object
|
||||
{
|
||||
S32 nameIndex;
|
||||
S32 numMeshes;
|
||||
S32 startMeshIndex; ///< Index into meshes array.
|
||||
S32 nodeIndex;
|
||||
|
||||
// computed at load
|
||||
S32 nextSibling;
|
||||
S32 firstDecal; // DEPRECATED
|
||||
};
|
||||
|
||||
/// A Sequence holds all the information necessary to perform a particular animation (sequence).
|
||||
///
|
||||
/// Sequences index a range of keyframes. Keyframes are assumed to be equally spaced in time.
|
||||
///
|
||||
/// Each node and object is either a member of the sequence or not. If not, they are set to
|
||||
/// default values when we switch to the sequence unless they are members of some other active sequence.
|
||||
/// Blended sequences "add" a transform to the current transform of a node. Any object animation of
|
||||
/// a blended sequence over-rides any existing object state. Blended sequences are always
|
||||
/// applied after non-blended sequences.
|
||||
struct Sequence
|
||||
{
|
||||
S32 nameIndex;
|
||||
S32 numKeyframes;
|
||||
F32 duration;
|
||||
S32 baseRotation;
|
||||
S32 baseTranslation;
|
||||
S32 baseScale;
|
||||
S32 baseObjectState;
|
||||
S32 baseDecalState; // DEPRECATED
|
||||
S32 firstGroundFrame;
|
||||
S32 numGroundFrames;
|
||||
S32 firstTrigger;
|
||||
S32 numTriggers;
|
||||
F32 toolBegin;
|
||||
|
||||
/// @name Bitsets
|
||||
/// These bitsets code whether this sequence cares about certain aspects of animation
|
||||
/// e.g., the rotation, translation, or scale of node transforms,
|
||||
/// or the visibility, frame or material frame of objects.
|
||||
/// @{
|
||||
|
||||
TSIntegerSet rotationMatters; ///< Set of nodes
|
||||
TSIntegerSet translationMatters; ///< Set of nodes
|
||||
TSIntegerSet scaleMatters; ///< Set of nodes
|
||||
TSIntegerSet visMatters; ///< Set of objects
|
||||
TSIntegerSet frameMatters; ///< Set of objects
|
||||
TSIntegerSet matFrameMatters; ///< Set of objects
|
||||
/// @}
|
||||
|
||||
S32 priority;
|
||||
U32 flags;
|
||||
U32 dirtyFlags; ///< determined at load time
|
||||
|
||||
/// @name Source Data
|
||||
/// Store some information about where the sequence data came from (used by
|
||||
/// TSShapeConstructor and the Shape Editor)
|
||||
/// @{
|
||||
struct SeqSourceData
|
||||
{
|
||||
String from; // The source sequence (ie. a DSQ file)
|
||||
S32 start; // The first frame in the source sequence
|
||||
S32 end; // The last frame in the source sequence
|
||||
S32 total; // The total number of frames in the source sequence
|
||||
String blendSeq; // The blend reference sequence
|
||||
S32 blendFrame; // The blend reference frame
|
||||
SeqSourceData() : from("\t"), start(0), end(0), total(0), blendSeq(""), blendFrame(0) { }
|
||||
} sourceData;
|
||||
|
||||
/// @name Flag Tests
|
||||
/// Each of these tests a different flag against the object's flag list
|
||||
/// to determine the attributes of the given object.
|
||||
/// @{
|
||||
|
||||
bool testFlags(U32 comp) const { return (flags&comp)!=0; }
|
||||
bool animatesScale() const { return testFlags(AnyScale); }
|
||||
bool animatesUniformScale() const { return testFlags(UniformScale); }
|
||||
bool animatesAlignedScale() const { return testFlags(AlignedScale); }
|
||||
bool animatesArbitraryScale() const { return testFlags(ArbitraryScale); }
|
||||
bool isBlend() const { return testFlags(Blend); }
|
||||
bool isCyclic() const { return testFlags(Cyclic); }
|
||||
bool makePath() const { return testFlags(MakePath); }
|
||||
/// @}
|
||||
|
||||
/// @name IO
|
||||
/// @{
|
||||
|
||||
void read(Stream *, bool readNameIndex = true);
|
||||
void write(Stream *, bool writeNameIndex = true) const;
|
||||
/// @}
|
||||
};
|
||||
|
||||
/// Describes state of an individual object. Includes everything in an object that can be
|
||||
/// controlled by animation.
|
||||
struct ObjectState
|
||||
{
|
||||
F32 vis;
|
||||
S32 frameIndex;
|
||||
S32 matFrameIndex;
|
||||
};
|
||||
|
||||
/// When time on a sequence advances past a certain point, a trigger takes effect and changes
|
||||
/// one of the state variables to on or off. (State variables found in TSShapeInstance::mTriggerStates)
|
||||
struct Trigger
|
||||
{
|
||||
enum TriggerStates
|
||||
{
|
||||
StateOn = BIT(31),
|
||||
InvertOnReverse = BIT(30),
|
||||
StateMask = BIT(30)-1
|
||||
};
|
||||
|
||||
U32 state; ///< One of TriggerStates
|
||||
F32 pos;
|
||||
};
|
||||
|
||||
/// Details are used for render detail selection.
|
||||
///
|
||||
/// As the projected size of the shape changes,
|
||||
/// a different node structure can be used (subShape) and a different objectDetail can be selected
|
||||
/// for each object drawn. Either of these two parameters can also stay constant, but presumably
|
||||
/// not both. If size is negative then the detail level will never be selected by the standard
|
||||
/// detail selection process. It will have to be selected by name. Such details are "utility
|
||||
/// details" because they exist to hold data (node positions or collision information) but not
|
||||
/// normally to be drawn. By default there will always be a "Ground" utility detail.
|
||||
///
|
||||
/// Note that this struct should always be 32bit aligned
|
||||
/// as its required by assembleShape/disassembleShape.
|
||||
struct Detail
|
||||
{
|
||||
S32 nameIndex;
|
||||
S32 subShapeNum;
|
||||
S32 objectDetailNum;
|
||||
F32 size;
|
||||
F32 averageError;
|
||||
F32 maxError;
|
||||
S32 polyCount;
|
||||
|
||||
/// These values are new autobillboard settings stored
|
||||
/// as part of the Detail struct in version 26 and above.
|
||||
/// @{
|
||||
|
||||
S32 bbDimension; ///< The size of the autobillboard image.
|
||||
S32 bbDetailLevel; ///< The detail to render as the autobillboard.
|
||||
U32 bbEquatorSteps; ///< The number of autobillboard images to capture around the equator.
|
||||
U32 bbPolarSteps; ///< The number of autobillboard images to capture along the pole.
|
||||
F32 bbPolarAngle; ///< The angle in radians at which the top/bottom autobillboard images should be displayed.
|
||||
U32 bbIncludePoles; ///< If non-zero then top and bottom images are generated for the autobillboard.
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
/// @name Collision Accelerators
|
||||
///
|
||||
/// For speeding up buildpolylist and support calls.
|
||||
/// @{
|
||||
struct ConvexHullAccelerator {
|
||||
S32 numVerts;
|
||||
Point3F* vertexList;
|
||||
Point3F* normalList;
|
||||
U8** emitStrings;
|
||||
};
|
||||
ConvexHullAccelerator* getAccelerator(S32 dl);
|
||||
/// @}
|
||||
|
||||
|
||||
/// @name Shape Vector Data
|
||||
/// @{
|
||||
|
||||
Vector<Node> nodes;
|
||||
Vector<Object> objects;
|
||||
Vector<ObjectState> objectStates;
|
||||
Vector<S32> subShapeFirstNode;
|
||||
Vector<S32> subShapeFirstObject;
|
||||
Vector<S32> detailFirstSkin;
|
||||
Vector<S32> subShapeNumNodes;
|
||||
Vector<S32> subShapeNumObjects;
|
||||
Vector<Detail> details;
|
||||
Vector<Quat16> defaultRotations;
|
||||
Vector<Point3F> defaultTranslations;
|
||||
|
||||
/// @}
|
||||
|
||||
/// These are set up at load time, but memory is allocated along with loaded data
|
||||
/// @{
|
||||
|
||||
Vector<S32> subShapeFirstTranslucentObject;
|
||||
Vector<TSMesh*> meshes;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Alpha Vectors
|
||||
/// these vectors describe how to transition between detail
|
||||
/// levels using alpha. "alpha-in" next detail as intraDL goes
|
||||
/// from alphaIn+alphaOut to alphaOut. "alpha-out" current
|
||||
/// detail level as intraDL goes from alphaOut to 0.
|
||||
/// @note
|
||||
/// - intraDL is at 1 when if shape were any closer to us we'd be at dl-1
|
||||
/// - intraDL is at 0 when if shape were any farther away we'd be at dl+1
|
||||
/// @{
|
||||
|
||||
Vector<F32> alphaIn;
|
||||
Vector<F32> alphaOut
|
||||
;
|
||||
/// @}
|
||||
|
||||
/// @name Resizeable vectors
|
||||
/// @{
|
||||
|
||||
Vector<Sequence> sequences;
|
||||
Vector<Quat16> nodeRotations;
|
||||
Vector<Point3F> nodeTranslations;
|
||||
Vector<F32> nodeUniformScales;
|
||||
Vector<Point3F> nodeAlignedScales;
|
||||
Vector<Quat16> nodeArbitraryScaleRots;
|
||||
Vector<Point3F> nodeArbitraryScaleFactors;
|
||||
Vector<Quat16> groundRotations;
|
||||
Vector<Point3F> groundTranslations;
|
||||
Vector<Trigger> triggers;
|
||||
Vector<TSLastDetail*> billboardDetails;
|
||||
Vector<ConvexHullAccelerator*> detailCollisionAccelerators;
|
||||
Vector<String> names;
|
||||
|
||||
/// @}
|
||||
|
||||
TSMaterialList * materialList;
|
||||
|
||||
/// @name Bounding
|
||||
/// @{
|
||||
|
||||
F32 radius;
|
||||
F32 tubeRadius;
|
||||
Point3F center;
|
||||
Box3F bounds;
|
||||
|
||||
/// @}
|
||||
|
||||
// various...
|
||||
U32 mExporterVersion;
|
||||
F32 mSmallestVisibleSize; ///< Computed at load time from details vector.
|
||||
S32 mSmallestVisibleDL; ///< @see mSmallestVisibleSize
|
||||
S32 mReadVersion; ///< File version that this shape was read from.
|
||||
U32 mFlags; ///< hasTranslucancy
|
||||
U32 data; ///< User-defined data storage.
|
||||
|
||||
/// If enabled detail selection will use the
|
||||
/// legacy screen error method for lod.
|
||||
/// @see setDetailFromScreenError
|
||||
bool mUseDetailFromScreenError;
|
||||
|
||||
// TODO: This would be nice as Tuple<>
|
||||
struct LodPair
|
||||
{
|
||||
S8 level; // -1 to 128
|
||||
U8 intra; // encoded 0 to 1
|
||||
|
||||
inline void set( S32 dl, F32 intraDL )
|
||||
{
|
||||
level = (S8)dl;
|
||||
intra = (S8)( intraDL * 255.0f );
|
||||
}
|
||||
|
||||
inline void get( S32 &dl, F32 &intraDL )
|
||||
{
|
||||
dl = level;
|
||||
intraDL = (F32)intra / 255.0f;
|
||||
}
|
||||
};
|
||||
|
||||
/// The lod lookup table where we mark down the detail
|
||||
/// level and intra-detail level for each pixel size.
|
||||
Vector<LodPair> mDetailLevelLookup;
|
||||
|
||||
/// The GFX vertex format for all detail meshes in the shape.
|
||||
/// @see initVertexFeatures()
|
||||
GFXVertexFormat mVertexFormat;
|
||||
|
||||
/// The GFX vertex size in bytes for all detail meshes in the shape.
|
||||
/// @see initVertexFeatures()
|
||||
U32 mVertSize;
|
||||
|
||||
/// Is true if this shape contains skin meshes.
|
||||
bool mHasSkinMesh;
|
||||
|
||||
bool mSequencesConstructed;
|
||||
|
||||
S8* mShapeData;
|
||||
U32 mShapeDataSize;
|
||||
|
||||
// shape class has few methods --
|
||||
// just constructor/destructor, io, and lookup methods
|
||||
|
||||
// constructor/destructor
|
||||
TSShape();
|
||||
~TSShape();
|
||||
void init();
|
||||
void initMaterialList(); ///< you can swap in a new material list, but call this if you do
|
||||
bool preloadMaterialList(const Torque::Path &path); ///< called to preload and validate the materials in the mat list
|
||||
|
||||
void setupBillboardDetails( const String &cachePath );
|
||||
|
||||
/// Called from init() to calcuate the GFX vertex features for
|
||||
/// all detail meshes in the shape.
|
||||
void initVertexFeatures();
|
||||
|
||||
bool getSequencesConstructed() const { return mSequencesConstructed; }
|
||||
void setSequencesConstructed(const bool c) { mSequencesConstructed = c; }
|
||||
|
||||
/// @name Lookup Animation Info
|
||||
/// indexed by keyframe number and offset (which object/node/decal
|
||||
/// of the animated objects/nodes/decals you want information for).
|
||||
/// @{
|
||||
|
||||
QuatF & getRotation(const Sequence & seq, S32 keyframeNum, S32 rotNum, QuatF *) const;
|
||||
const Point3F & getTranslation(const Sequence & seq, S32 keyframeNum, S32 tranNum) const;
|
||||
F32 getUniformScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum) const;
|
||||
const Point3F & getAlignedScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum) const;
|
||||
TSScale & getArbitraryScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum, TSScale *) const;
|
||||
const ObjectState & getObjectState(const Sequence & seq, S32 keyframeNum, S32 objectNum) const;
|
||||
/// @}
|
||||
|
||||
/// build LOS collision detail
|
||||
void computeAccelerator(S32 dl);
|
||||
bool buildConvexHull(S32 dl) const;
|
||||
void computeBounds(S32 dl, Box3F & bounds) const; // uses default transforms to compute bounding box around a detail level
|
||||
// see like named method on shapeInstance if you want to use animated transforms
|
||||
|
||||
/// Used to find collision detail meshes in the DTS.
|
||||
///
|
||||
/// @param useVisibleMesh If true return the highest visible detail level.
|
||||
/// @param outDetails The output detail index vector.
|
||||
/// @param outLOSDetails The optional output LOS detail vector.
|
||||
///
|
||||
void findColDetails( bool useVisibleMesh, Vector<S32> *outDetails, Vector<S32> *outLOSDetails ) const;
|
||||
|
||||
/// Builds a physics collision shape at the requested scale.
|
||||
///
|
||||
/// If using the visible mesh one or more triangle meshes are created
|
||||
/// from the first visible detail level.
|
||||
///
|
||||
/// If using collision meshes we look for mesh names prefixed with the
|
||||
/// following hints:
|
||||
//
|
||||
/// "colbox"
|
||||
/// "colsphere"
|
||||
/// "colcapsule"
|
||||
/// "colmesh"
|
||||
///
|
||||
/// In the case of the primitives the mesh bounding box is used to generate
|
||||
/// a box, sphere, or capsule collision shape. The "colmesh" will create a
|
||||
/// concave triangle mesh for collision.
|
||||
///
|
||||
/// Any other named collision shape is interpreted as a regular convex hull.
|
||||
///
|
||||
/// @return The collision object or NULL if no collision data could be generated.
|
||||
///
|
||||
PhysicsCollision* buildColShape( bool useVisibleMesh, const Point3F &scale );
|
||||
|
||||
/// Like buildColShape except we build one PhysicsCollision object per
|
||||
/// collision node.
|
||||
///
|
||||
/// Results are returned by filling in the CollisionShapeInfo Vector, which also
|
||||
/// specifies the collision node index for each PhysicsCollision built.
|
||||
///
|
||||
void buildColShapes( bool useVisibleMesh, const Point3F &scale, Vector< CollisionShapeInfo > *list );
|
||||
|
||||
/// For internal use.
|
||||
PhysicsCollision* _buildColShapes( bool useVisibleMesh, const Point3F &scale, Vector< CollisionShapeInfo > *list, bool perMesh );
|
||||
|
||||
/// @name Lookup Methods
|
||||
/// @{
|
||||
|
||||
/// Returns index into the name vector that equals the passed name.
|
||||
S32 findName( const String &name ) const;
|
||||
|
||||
/// Returns name string at the passed name vector index.
|
||||
const String& getName( S32 nameIndex ) const;
|
||||
|
||||
/// Returns name string for mesh at the passed index.
|
||||
const String& getMeshName( S32 meshIndex ) const;
|
||||
|
||||
/// Returns name string for node at the passed index.
|
||||
const String& getNodeName( S32 nodeIndex ) const;
|
||||
|
||||
/// Returns name string for sequence at the passed index.
|
||||
const String& getSequenceName( S32 seqIndex ) const;
|
||||
|
||||
S32 getTargetCount() const;
|
||||
const String& getTargetName( S32 mapToNameIndex ) const;
|
||||
|
||||
S32 findNode(S32 nameIndex) const;
|
||||
S32 findNode(const String &name) const { return findNode(findName(name)); }
|
||||
|
||||
S32 findObject(S32 nameIndex) const;
|
||||
S32 findObject(const String &name) const { return findObject(findName(name)); }
|
||||
|
||||
S32 findDetail(S32 nameIndex) const;
|
||||
S32 findDetail(const String &name) const { return findDetail(findName(name)); }
|
||||
S32 findDetailBySize(S32 size) const;
|
||||
|
||||
S32 findSequence(S32 nameIndex) const;
|
||||
S32 findSequence(const String &name) const { return findSequence(findName(name)); }
|
||||
|
||||
S32 getSubShapeForNode(S32 nodeIndex);
|
||||
S32 getSubShapeForObject(S32 objIndex);
|
||||
void getSubShapeDetails(S32 subShapeIndex, Vector<S32>& validDetails);
|
||||
|
||||
void getNodeWorldTransform(S32 nodeIndex, MatrixF* mat) const;
|
||||
void getNodeKeyframe(S32 nodeIndex, const TSShape::Sequence& seq, S32 keyframe, MatrixF* mat) const;
|
||||
void getNodeObjects(S32 nodeIndex, Vector<S32>& nodeObjects);
|
||||
void getNodeChildren(S32 nodeIndex, Vector<S32>& nodeChildren);
|
||||
|
||||
void getObjectDetails(S32 objIndex, Vector<S32>& objDetails);
|
||||
|
||||
bool findMeshIndex(const String &meshName, S32& objIndex, S32& meshIndex);
|
||||
TSMesh* findMesh(const String &meshName);
|
||||
|
||||
bool hasTranslucency() const { return (mFlags & HasTranslucency)!=0; }
|
||||
|
||||
const GFXVertexFormat* getVertexFormat() const { return &mVertexFormat; }
|
||||
|
||||
U32 getVertexSize() const { return mVertSize; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Alpha Transitions
|
||||
/// These control default values for alpha transitions between detail levels
|
||||
/// @{
|
||||
|
||||
static F32 smAlphaOutLastDetail;
|
||||
static F32 smAlphaInBillboard;
|
||||
static F32 smAlphaOutBillboard;
|
||||
static F32 smAlphaInDefault;
|
||||
static F32 smAlphaOutDefault;
|
||||
/// @}
|
||||
|
||||
/// don't load this many of the highest detail levels (although we always
|
||||
/// load one renderable detail if there is one)
|
||||
static S32 smNumSkipLoadDetails;
|
||||
|
||||
/// by default we initialize shape when we read...
|
||||
static bool smInitOnRead;
|
||||
|
||||
/// @name Version Info
|
||||
/// @{
|
||||
|
||||
/// Most recent version...the one we write
|
||||
static S32 smVersion;
|
||||
/// Version currently being read, only valid during read
|
||||
static S32 smReadVersion;
|
||||
static const U32 smMostRecentExporterVersion;
|
||||
///@}
|
||||
|
||||
/// @name Persist Methods
|
||||
/// Methods for saving/loading shapes to/from streams
|
||||
/// @{
|
||||
|
||||
bool canWriteOldFormat() const;
|
||||
void write(Stream *, bool saveOldFormat=false);
|
||||
bool read(Stream *);
|
||||
void readOldShape(Stream * s, S32 * &, S16 * &, S8 * &, S32 &, S32 &, S32 &);
|
||||
void writeName(Stream *, S32 nameIndex);
|
||||
S32 readName(Stream *, bool addName);
|
||||
|
||||
/// Initializes our TSShape to be ready to receive put mesh data
|
||||
void createEmptyShape();
|
||||
|
||||
void exportSequences(Stream *);
|
||||
void exportSequence(Stream * s, const TSShape::Sequence& seq, bool saveOldFormat);
|
||||
bool importSequences(Stream *, const String& sequencePath);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Persist Helper Functions
|
||||
/// @{
|
||||
|
||||
static TSShapeAlloc smTSAlloc;
|
||||
|
||||
void fixEndian(S32 *, S16 *, S8 *, S32, S32, S32);
|
||||
/// @}
|
||||
|
||||
/// @name Memory Buffer Transfer Methods
|
||||
/// uses TSShape::Alloc structure
|
||||
/// @{
|
||||
|
||||
void assembleShape();
|
||||
void disassembleShape();
|
||||
///@}
|
||||
|
||||
/// mem buffer transfer helper (indicate when we don't want to include a particular mesh/decal)
|
||||
bool checkSkip(S32 meshNum, S32 & curObject, S32 skipDL);
|
||||
|
||||
void fixupOldSkins(S32 numMeshes, S32 numSkins, S32 numDetails, S32 * detailFirstSkin, S32 * detailNumSkins);
|
||||
|
||||
/// @name Shape Editing
|
||||
/// @{
|
||||
S32 addName(const String& name);
|
||||
bool removeName(const String& name);
|
||||
void updateSmallestVisibleDL();
|
||||
S32 addDetail(const String& dname, S32 size, S32 subShapeNum);
|
||||
|
||||
S32 addImposter( const String& cachePath,
|
||||
S32 size,
|
||||
S32 numEquatorSteps,
|
||||
S32 numPolarSteps,
|
||||
S32 dl,
|
||||
S32 dim,
|
||||
bool includePoles,
|
||||
F32 polarAngle );
|
||||
bool removeImposter();
|
||||
|
||||
bool renameNode(const String& oldName, const String& newName);
|
||||
bool renameObject(const String& oldName, const String& newName);
|
||||
bool renameDetail(const String& oldName, const String& newName);
|
||||
bool renameSequence(const String& oldName, const String& newName);
|
||||
|
||||
bool setNodeTransform(const String& name, const Point3F& pos, const QuatF& rot);
|
||||
bool addNode(const String& name, const String& parentName, const Point3F& pos, const QuatF& rot);
|
||||
bool removeNode(const String& name);
|
||||
|
||||
S32 addObject(const String& objName, S32 subShapeIndex);
|
||||
void addMeshToObject(S32 objIndex, S32 meshIndex, TSMesh* mesh);
|
||||
void removeMeshFromObject(S32 objIndex, S32 meshIndex);
|
||||
bool setObjectNode(const String& objName, const String& nodeName);
|
||||
bool removeObject(const String& objName);
|
||||
|
||||
TSMesh* copyMesh( const TSMesh* srcMesh ) const;
|
||||
bool addMesh(TSShape* srcShape, const String& srcMeshName, const String& meshName);
|
||||
bool addMesh(TSMesh* mesh, const String& meshName);
|
||||
bool setMeshSize(const String& meshName, S32 size);
|
||||
bool removeMesh(const String& meshName);
|
||||
|
||||
S32 setDetailSize(S32 oldSize, S32 newSize);
|
||||
bool removeDetail(S32 size);
|
||||
|
||||
bool addSequence(const Torque::Path& path, const String& fromSeq, const String& name, S32 startFrame, S32 endFrame, bool padRotKeys, bool padTransKeys);
|
||||
bool removeSequence(const String& name);
|
||||
|
||||
bool addTrigger(const String& seqName, S32 keyframe, S32 state);
|
||||
bool removeTrigger(const String& seqName, S32 keyframe, S32 state);
|
||||
|
||||
bool setSequenceBlend(const String& seqName, bool blend, const String& blendRefSeqName, S32 blendRefFrame);
|
||||
bool setSequenceGroundSpeed(const String& seqName, const Point3F& trans, const Point3F& rot);
|
||||
/// @}
|
||||
};
|
||||
|
||||
|
||||
#define TSNode TSShape::Node
|
||||
#define TSObject TSShape::Object
|
||||
#define TSSequence TSShape::Sequence
|
||||
#define TSDetail TSShape::Detail
|
||||
|
||||
inline QuatF & TSShape::getRotation(const Sequence & seq, S32 keyframeNum, S32 rotNum, QuatF * quat) const
|
||||
{
|
||||
return nodeRotations[seq.baseRotation + rotNum*seq.numKeyframes + keyframeNum].getQuatF(quat);
|
||||
}
|
||||
|
||||
inline const Point3F & TSShape::getTranslation(const Sequence & seq, S32 keyframeNum, S32 tranNum) const
|
||||
{
|
||||
return nodeTranslations[seq.baseTranslation + tranNum*seq.numKeyframes + keyframeNum];
|
||||
}
|
||||
|
||||
inline F32 TSShape::getUniformScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum) const
|
||||
{
|
||||
return nodeUniformScales[seq.baseScale + scaleNum*seq.numKeyframes + keyframeNum];
|
||||
}
|
||||
|
||||
inline const Point3F & TSShape::getAlignedScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum) const
|
||||
{
|
||||
return nodeAlignedScales[seq.baseScale + scaleNum*seq.numKeyframes + keyframeNum];
|
||||
}
|
||||
|
||||
inline TSScale & TSShape::getArbitraryScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum, TSScale * scale) const
|
||||
{
|
||||
nodeArbitraryScaleRots[seq.baseScale + scaleNum*seq.numKeyframes + keyframeNum].getQuatF(&scale->mRotate);
|
||||
scale->mScale = nodeArbitraryScaleFactors[seq.baseScale + scaleNum*seq.numKeyframes + keyframeNum];
|
||||
return *scale;
|
||||
}
|
||||
|
||||
inline const TSShape::ObjectState & TSShape::getObjectState(const Sequence & seq, S32 keyframeNum, S32 objectNum) const
|
||||
{
|
||||
return objectStates[seq.baseObjectState + objectNum*seq.numKeyframes + keyframeNum];
|
||||
}
|
||||
|
||||
#endif
|
||||
229
Engine/source/ts/tsShapeAlloc.cpp
Normal file
229
Engine/source/ts/tsShapeAlloc.cpp
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "ts/tsShapeAlloc.h"
|
||||
|
||||
#define readOnly() AssertFatal(mMode==TSShapeAlloc::ReadMode, "TSShapeAlloc: write-only function called when reading")
|
||||
#define writeOnly() AssertFatal(mMode==TSShapeAlloc::WriteMode,"TSShapeAlloc: read-only function called when writing")
|
||||
|
||||
void TSShapeAlloc::setRead(S32 * memBuffer32, S16 * memBuffer16, S8 * memBuffer8, bool clear)
|
||||
{
|
||||
mMemBuffer32 = memBuffer32;
|
||||
mMemBuffer16 = memBuffer16;
|
||||
mMemBuffer8 = memBuffer8 ;
|
||||
|
||||
mMemGuard32 = 0;
|
||||
mMemGuard16 = 0;
|
||||
mMemGuard8 = 0;
|
||||
|
||||
mSaveGuard32 = 0;
|
||||
mSaveGuard16 = 0;
|
||||
mSaveGuard8 = 0;
|
||||
|
||||
if (clear)
|
||||
{
|
||||
mDest = NULL;
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
setSkipMode(false);
|
||||
mMode = TSShapeAlloc::ReadMode;
|
||||
}
|
||||
|
||||
void TSShapeAlloc::setWrite()
|
||||
{
|
||||
mMemBuffer32 = 0;
|
||||
mMemBuffer16 = 0;
|
||||
mMemBuffer8 = 0;
|
||||
|
||||
mSize32 = mFullSize32 = 0;
|
||||
mSize16 = mFullSize16 = 0;
|
||||
mSize8 = mFullSize8 = 0;
|
||||
|
||||
mMemGuard32 = 0;
|
||||
mMemGuard16 = 0;
|
||||
mMemGuard8 = 0;
|
||||
|
||||
setSkipMode(false); // doesn't really do anything here...
|
||||
mMode = TSShapeAlloc::WriteMode;
|
||||
}
|
||||
|
||||
void TSShapeAlloc::doAlloc()
|
||||
{
|
||||
readOnly();
|
||||
|
||||
mDest = new S8[mSize];
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
void TSShapeAlloc::align32()
|
||||
{
|
||||
readOnly();
|
||||
|
||||
S32 aligned = mSize+3 & (~0x3);
|
||||
allocShape8(aligned-mSize);
|
||||
}
|
||||
|
||||
#define IMPLEMENT_ALLOC(suffix,type) \
|
||||
\
|
||||
type TSShapeAlloc::get##suffix() \
|
||||
{ \
|
||||
readOnly(); \
|
||||
return *(mMemBuffer##suffix++); \
|
||||
} \
|
||||
\
|
||||
void TSShapeAlloc::get##suffix(type * dest, S32 num) \
|
||||
{ \
|
||||
readOnly(); \
|
||||
dMemcpy(dest,mMemBuffer##suffix,sizeof(type)*num); \
|
||||
mMemBuffer##suffix += num; \
|
||||
} \
|
||||
\
|
||||
type * TSShapeAlloc::allocShape##suffix(S32 num) \
|
||||
{ \
|
||||
readOnly(); \
|
||||
type * ret = (type*) mDest; \
|
||||
if (mDest) \
|
||||
mDest += mMult*num*sizeof(type); \
|
||||
mSize += sizeof(type)*mMult*num; \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
type * TSShapeAlloc::getPointer##suffix(S32 num) \
|
||||
{ \
|
||||
readOnly(); \
|
||||
type * ret = (type*)mMemBuffer##suffix; \
|
||||
mMemBuffer##suffix += num; \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
type * TSShapeAlloc::copyToShape##suffix(S32 num, bool returnSomething) \
|
||||
{ \
|
||||
readOnly(); \
|
||||
type * ret = (!returnSomething || mDest) ? (type*)mDest : mMemBuffer##suffix; \
|
||||
if (mDest) \
|
||||
{ \
|
||||
dMemcpy((S8*)mDest,(S8*)mMemBuffer##suffix, \
|
||||
mMult*num*sizeof(type)); \
|
||||
mDest += mMult*num*sizeof(type); \
|
||||
} \
|
||||
mMemBuffer##suffix += num; \
|
||||
mSize += sizeof(type)*mMult*num; \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
bool TSShapeAlloc::checkGuard##suffix() \
|
||||
{ \
|
||||
readOnly(); \
|
||||
mSaveGuard##suffix=get##suffix(); \
|
||||
bool ret = (mSaveGuard##suffix==mMemGuard##suffix); \
|
||||
mMemGuard##suffix += 1; \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
type TSShapeAlloc::getPrevGuard##suffix() \
|
||||
{ \
|
||||
readOnly(); \
|
||||
return mMemGuard##suffix - 1; \
|
||||
} \
|
||||
\
|
||||
type TSShapeAlloc::getSaveGuard##suffix() \
|
||||
{ \
|
||||
readOnly(); \
|
||||
return mSaveGuard##suffix; \
|
||||
} \
|
||||
\
|
||||
type * TSShapeAlloc::getBuffer##suffix() \
|
||||
{ \
|
||||
writeOnly(); \
|
||||
return mMemBuffer##suffix; \
|
||||
} \
|
||||
\
|
||||
S32 TSShapeAlloc::getBufferSize##suffix() \
|
||||
{ \
|
||||
writeOnly(); \
|
||||
return mSize##suffix; \
|
||||
} \
|
||||
\
|
||||
type * TSShapeAlloc::extend##suffix(S32 add) \
|
||||
{ \
|
||||
writeOnly(); \
|
||||
if (mSize##suffix+add>mFullSize##suffix) \
|
||||
{ \
|
||||
S32 numPages = 1+(mFullSize##suffix+add)/TSShapeAlloc::PageSize; \
|
||||
mFullSize##suffix = numPages*TSShapeAlloc::PageSize; \
|
||||
type * temp = new type[mFullSize##suffix]; \
|
||||
dMemcpy(temp,mMemBuffer##suffix, mSize##suffix * sizeof(type)); \
|
||||
delete [] mMemBuffer##suffix; \
|
||||
mMemBuffer##suffix = temp; \
|
||||
} \
|
||||
type * ret = mMemBuffer##suffix + mSize##suffix; \
|
||||
mSize##suffix += add; \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
type TSShapeAlloc::set##suffix(type entry) \
|
||||
{ \
|
||||
writeOnly(); \
|
||||
*extend##suffix(1) = entry; \
|
||||
return entry; \
|
||||
} \
|
||||
\
|
||||
void TSShapeAlloc::copyToBuffer##suffix(type * entries, S32 count) \
|
||||
{ \
|
||||
writeOnly(); \
|
||||
if (entries) \
|
||||
dMemcpy((U8*)extend##suffix(count),(U8*)entries,count*sizeof(type)); \
|
||||
else \
|
||||
dMemset((U8*)extend##suffix(count),0,count*sizeof(type)); \
|
||||
} \
|
||||
\
|
||||
void TSShapeAlloc::setGuard##suffix() \
|
||||
{ \
|
||||
writeOnly(); \
|
||||
set##suffix(mMemGuard##suffix); \
|
||||
mMemGuard##suffix += 1; \
|
||||
}
|
||||
|
||||
IMPLEMENT_ALLOC(32,S32)
|
||||
IMPLEMENT_ALLOC(16,S16)
|
||||
IMPLEMENT_ALLOC(8,S8)
|
||||
|
||||
void TSShapeAlloc::checkGuard()
|
||||
{
|
||||
bool check32 = checkGuard32();
|
||||
bool check16 = checkGuard16();
|
||||
bool check8 = checkGuard8();
|
||||
|
||||
AssertFatal(check32,avar("TSShapeAlloc::checkGuard32: found %i, wanted %i",getSaveGuard32(),getPrevGuard32()));
|
||||
AssertFatal(check16,avar("TSShapeAlloc::checkGuard16: found %i, wanted %i",getSaveGuard16(),getPrevGuard16()));
|
||||
AssertFatal(check8 ,avar("TSShapeAlloc::checkGuard8: found %i, wanted %i",getSaveGuard8() ,getPrevGuard8()));
|
||||
}
|
||||
|
||||
void TSShapeAlloc::setGuard()
|
||||
{
|
||||
setGuard32();
|
||||
setGuard16();
|
||||
setGuard8();
|
||||
}
|
||||
|
||||
|
||||
156
Engine/source/ts/tsShapeAlloc.h
Normal file
156
Engine/source/ts/tsShapeAlloc.h
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSSHAPEALLOC_H_
|
||||
#define _TSSHAPEALLOC_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
|
||||
/// Alloc structure used in the reading/writing of shapes.
|
||||
///
|
||||
/// In read mode we assemble contents of 32-bit, 16-bit, and 8-bit buffers
|
||||
/// into a single destination buffer.
|
||||
///
|
||||
/// In write mode we dissemble a stream of memory (which may be scattered in physical memory)
|
||||
/// into 32-bit, 16-bit, 8-bit, Point3F, and Point2F buffers using function calls similar
|
||||
/// to the read calls.
|
||||
///
|
||||
/// Read usage:
|
||||
/// 1. call "setRead" with each incoming memory buffers and clear=true.
|
||||
/// 2. run through set of operations for allocating and transfering memory to target buffer
|
||||
/// these are the operations under "DECLARE_ALLOC" that call readOnly in the .cc file.
|
||||
/// 3. call "doAlloc" to create buffer exactly as large as needed.
|
||||
/// 4. repeat step 1 & 2 to do the actual transfer of memory, except with clear=false
|
||||
/// (note: first time through nothing was copied to the shape, we only kept track
|
||||
/// of the size of the transfer).
|
||||
/// 5. call getBuffer to get the target (destination buffer)
|
||||
///
|
||||
/// write usage:
|
||||
/// 1. call "setWrite" (no parameters).
|
||||
/// 2. run through set of operations for allocating and transfering memory to internal buffers
|
||||
/// these are the operations under "DECLARE_ALLOC" that call writeOnly in the .cc file.
|
||||
/// 3. call getBuffer32 and getBufferSize32 to get 32-bit buffer and size. Similarly for
|
||||
/// 16-bit, 8-bit (getBuffer16, getBuffer8).
|
||||
///
|
||||
/// TSShape::assesmbleShape and TSShape::dissembleShape can be used as examples
|
||||
class TSShapeAlloc
|
||||
{
|
||||
S32 mMode; ///< read or write
|
||||
|
||||
/// reading and writing (when reading these are the input; when writing these are the output)
|
||||
S32 * mMemBuffer32;
|
||||
S16 * mMemBuffer16;
|
||||
S8 * mMemBuffer8;
|
||||
|
||||
/// for writing only...
|
||||
S32 mSize32;
|
||||
S32 mSize16;
|
||||
S32 mSize8;
|
||||
S32 mFullSize32;
|
||||
S32 mFullSize16;
|
||||
S32 mFullSize8;
|
||||
|
||||
/// reading and writing...
|
||||
S32 mMemGuard32;
|
||||
S16 mMemGuard16;
|
||||
S8 mMemGuard8;
|
||||
|
||||
/// reading
|
||||
S32 mSaveGuard32;
|
||||
S16 mSaveGuard16;
|
||||
S8 mSaveGuard8;
|
||||
|
||||
/// reading only...this is the output
|
||||
S8 * mDest;
|
||||
S32 mSize;
|
||||
S32 mMult; ///< mult incoming sizes by this (when 0, then mDest doesn't grow --> skip mode)
|
||||
|
||||
public:
|
||||
|
||||
enum { ReadMode = 0, WriteMode = 1, PageSize = 1024 }; ///< PageSize must be multiple of 4 so that we can always
|
||||
///< "over-read" up to next dword
|
||||
|
||||
void setRead(S32 * buff32, S16 * buff16, S8 * buff8, bool clear);
|
||||
void setWrite();
|
||||
|
||||
// reading only...
|
||||
void doAlloc();
|
||||
void align32(); ///< align on dword boundary
|
||||
S8 * getBuffer() { return mDest; }
|
||||
S32 getSize() { return mSize; }
|
||||
void setSkipMode(bool skip) { mMult = skip ? 0 : 1; }
|
||||
|
||||
/// @name Reading Operations:
|
||||
///
|
||||
/// get(): reads one or more entries of type from input buffer (doesn't affect output buffer)
|
||||
///
|
||||
/// copyToShape(): copies entries of type from input buffer to output buffer
|
||||
///
|
||||
/// allocShape(): creates room for entries of type in output buffer (no effect on input buffer)
|
||||
///
|
||||
/// getPointer(): gets pointer to next entries of type in input buffer (no effect on input buffer)
|
||||
///
|
||||
/// @note all operations advance current "position" of input and output buffers
|
||||
/// writing operations:
|
||||
///
|
||||
/// set(): adds one entry to appropriate buffer
|
||||
///
|
||||
/// copyToBuffer(): adds count entries to approrpiate buffer
|
||||
///
|
||||
/// getBuffer(): returns associated buffer (i.e., getBuffer32 gets 32bit buffer)
|
||||
///
|
||||
/// getBufferSize(): returns size of associated buffer
|
||||
///
|
||||
/// @{
|
||||
|
||||
#define DECLARE_ALLOC(suffix,type) \
|
||||
type get##suffix(); \
|
||||
void get##suffix(type*,S32); \
|
||||
type * copyToShape##suffix(S32,bool returnSomething=false); \
|
||||
type * getPointer##suffix(S32); \
|
||||
type * allocShape##suffix(S32); \
|
||||
bool checkGuard##suffix(); \
|
||||
type getPrevGuard##suffix(); \
|
||||
type getSaveGuard##suffix(); \
|
||||
type * getBuffer##suffix(); \
|
||||
S32 getBufferSize##suffix(); \
|
||||
void setGuard##suffix(); \
|
||||
type * extend##suffix(S32); \
|
||||
type set##suffix(type); \
|
||||
void copyToBuffer##suffix(type*,S32);
|
||||
|
||||
|
||||
DECLARE_ALLOC(32,S32)
|
||||
DECLARE_ALLOC(16,S16)
|
||||
DECLARE_ALLOC(8,S8)
|
||||
/// @}
|
||||
|
||||
void checkGuard();
|
||||
void setGuard();
|
||||
};
|
||||
|
||||
#endif // _H_TS_SHAPE_ALLOC_
|
||||
3254
Engine/source/ts/tsShapeConstruct.cpp
Normal file
3254
Engine/source/ts/tsShapeConstruct.cpp
Normal file
File diff suppressed because it is too large
Load diff
406
Engine/source/ts/tsShapeConstruct.h
Normal file
406
Engine/source/ts/tsShapeConstruct.h
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSSHAPECONSTRUCT_H_
|
||||
#define _TSSHAPECONSTRUCT_H_
|
||||
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#endif
|
||||
#ifndef _MTRANSFORM_H_
|
||||
#include "math/mTransform.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPE_H_
|
||||
#include "ts/tsShape.h"
|
||||
#endif
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
#ifndef _COLLADA_UTILS_H_
|
||||
#include "ts/collada/colladaUtils.h"
|
||||
#endif
|
||||
|
||||
/// This class allows an artist to export their animations for the model
|
||||
/// into the .dsq format. This class in particular matches up the model
|
||||
/// with the .dsqs to create a nice animated model.
|
||||
class TSShapeConstructor : public SimObject
|
||||
{
|
||||
typedef SimObject Parent;
|
||||
|
||||
public:
|
||||
struct ChangeSet
|
||||
{
|
||||
enum eCommandType
|
||||
{
|
||||
CmdAddNode,
|
||||
CmdRemoveNode,
|
||||
CmdRenameNode,
|
||||
CmdSetNodeTransform,
|
||||
CmdSetNodeParent,
|
||||
|
||||
CmdAddMesh,
|
||||
CmdAddPrimitive,
|
||||
CmdRemoveMesh,
|
||||
CmdSetMeshSize,
|
||||
CmdSetMeshType,
|
||||
CmdSetMeshMaterial,
|
||||
|
||||
CmdRemoveObject,
|
||||
CmdRenameObject,
|
||||
CmdSetObjectNode,
|
||||
CmdSetBounds,
|
||||
|
||||
CmdRenameDetailLevel,
|
||||
CmdRemoveDetailLevel,
|
||||
CmdSetDetailLevelSize,
|
||||
CmdAddImposter,
|
||||
CmdRemoveImposter,
|
||||
CmdAddCollisionDetail,
|
||||
|
||||
CmdAddSequence,
|
||||
CmdRemoveSequence,
|
||||
CmdRenameSequence,
|
||||
CmdSetSequenceCyclic,
|
||||
CmdSetSequenceBlend,
|
||||
CmdSetSequencePriority,
|
||||
CmdSetSequenceGroundSpeed,
|
||||
|
||||
CmdAddTrigger,
|
||||
CmdRemoveTrigger,
|
||||
|
||||
CmdInvalid
|
||||
};
|
||||
|
||||
struct Command
|
||||
{
|
||||
eCommandType type; // Command type
|
||||
StringTableEntry name; // Command name
|
||||
String argv[10]; // Command arguments
|
||||
S32 argc; // Number of arguments
|
||||
Command() : type(CmdInvalid), name(0), argc(0) { }
|
||||
Command( const char* _name )
|
||||
: type(CmdInvalid), argc(0)
|
||||
{
|
||||
name = StringTable->insert( _name );
|
||||
}
|
||||
|
||||
// Helper functions to fill in the command arguments
|
||||
inline void addArgs() { }
|
||||
|
||||
template< typename A >
|
||||
inline void addArgs( A a )
|
||||
{
|
||||
argv[argc++] = EngineMarshallData( a );
|
||||
}
|
||||
template< typename A, typename B > void addArgs( A a, B b )
|
||||
{
|
||||
addArgs( a );
|
||||
addArgs( b );
|
||||
}
|
||||
template< typename A, typename B, typename C >
|
||||
inline void addArgs( A a, B b, C c )
|
||||
{
|
||||
addArgs( a );
|
||||
addArgs( b, c );
|
||||
}
|
||||
template< typename A, typename B, typename C, typename D >
|
||||
inline void addArgs( A a, B b, C c, D d )
|
||||
{
|
||||
addArgs( a );
|
||||
addArgs( b, c, d );
|
||||
}
|
||||
template< typename A, typename B, typename C, typename D, typename E >
|
||||
inline void addArgs( A a, B b, C c, D d, E e )
|
||||
{
|
||||
addArgs( a );
|
||||
addArgs( b, c, d, e );
|
||||
}
|
||||
template< typename A, typename B, typename C, typename D, typename E, typename F >
|
||||
inline void addArgs( A a, B b, C c, D d, E e, F f )
|
||||
{
|
||||
addArgs( a );
|
||||
addArgs( b, c, d, e, f );
|
||||
}
|
||||
template< typename A, typename B, typename C, typename D, typename E, typename F, typename G >
|
||||
inline void addArgs( A a, B b, C c, D d, E e, F f, G g )
|
||||
{
|
||||
addArgs( a );
|
||||
addArgs( b, c, d, e, f, g );
|
||||
}
|
||||
template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H >
|
||||
inline void addArgs( A a, B b, C c, D d, E e, F f, G g, H h )
|
||||
{
|
||||
addArgs( a );
|
||||
addArgs( b, c, d, e, f, g, h );
|
||||
}
|
||||
template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I >
|
||||
inline void addArgs( A a, B b, C c, D d, E e, F f, G g, H h, I i )
|
||||
{
|
||||
addArgs( a );
|
||||
addArgs( b, c, d, e, f, g, h, i );
|
||||
}
|
||||
template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J >
|
||||
inline void addArgs( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j )
|
||||
{
|
||||
addArgs( a );
|
||||
addArgs( b, c, d, e, f, g, h, i, j );
|
||||
}
|
||||
};
|
||||
|
||||
Vector<Command> mCommands;
|
||||
|
||||
eCommandType getCmdType(const char* name);
|
||||
void clear() { mCommands.clear(); }
|
||||
bool empty() { return mCommands.empty(); }
|
||||
|
||||
void add( Command& cmd );
|
||||
|
||||
// These methods handle change set optimisation based on the newly added command
|
||||
bool addCmd_setNodeParent( const Command& newCmd );
|
||||
bool addCmd_setNodeTransform( const Command& newCmd );
|
||||
bool addCmd_renameNode( const Command& newCmd );
|
||||
bool addCmd_removeNode( const Command& newCmd );
|
||||
|
||||
bool addCmd_setMeshSize( const Command& newCmd );
|
||||
bool addCmd_setMeshType( const Command& newCmd );
|
||||
bool addCmd_setMeshMaterial( const Command& newCmd );
|
||||
bool addCmd_removeMesh( const Command& newCmd );
|
||||
|
||||
bool addCmd_setObjectNode( const Command& newCmd );
|
||||
bool addCmd_renameObject( const Command& newCmd );
|
||||
bool addCmd_removeObject( const Command& newCmd );
|
||||
bool addCmd_setBounds( const Command& newCmd );
|
||||
|
||||
bool addCmd_renameDetailLevel( const Command& newCmd );
|
||||
bool addCmd_removeDetailLevel( const Command& newCmd );
|
||||
bool addCmd_setDetailSize( const Command& newCmd );
|
||||
bool addCmd_addImposter( const Command& newCmd );
|
||||
bool addCmd_removeImposter( const Command& newCmd );
|
||||
|
||||
bool addCmd_addSequence( Command& newCmd );
|
||||
bool addCmd_setSequencePriority( const Command& newCmd );
|
||||
bool addCmd_setSequenceGroundSpeed( const Command& newCmd );
|
||||
bool addCmd_setSequenceCyclic( const Command& newCmd );
|
||||
bool addCmd_setSequenceBlend( const Command& newCmd );
|
||||
bool addCmd_renameSequence( const Command& newCmd );
|
||||
bool addCmd_removeSequence( const Command& newCmd );
|
||||
|
||||
bool addCmd_addTrigger( const Command& newCmd );
|
||||
bool addCmd_removeTrigger( const Command& newCmd );
|
||||
|
||||
void write(TSShape* shape, Stream& stream, const String& savePath);
|
||||
};
|
||||
|
||||
static const int MaxLegacySequences = 127;
|
||||
|
||||
protected:
|
||||
FileName mShapePath;
|
||||
Vector<FileName> mSequences;
|
||||
ChangeSet mChangeSet;
|
||||
|
||||
static bool addSequenceFromField( void *obj, const char *index, const char *data );
|
||||
|
||||
static void _onTSShapeLoaded( Resource< TSShape >& shape );
|
||||
static void _onTSShapeUnloaded( const Torque::Path& path, TSShape* shape );
|
||||
|
||||
static ResourceRegisterPostLoadSignal< TSShape > _smAutoLoad;
|
||||
static ResourceRegisterUnloadSignal< TSShape > _smAutoUnload;
|
||||
|
||||
/// @name Callbacks
|
||||
///@{
|
||||
DECLARE_CALLBACK( void, onLoad, () );
|
||||
DECLARE_CALLBACK( void, onUnload, () );
|
||||
///@}
|
||||
|
||||
virtual void _onLoad( TSShape* shape );
|
||||
virtual void _onUnload();
|
||||
|
||||
public:
|
||||
|
||||
TSShape* mShape; // Edited shape; NULL while not loaded; not a Resource<TSShape> as we don't want it to prevent from unloading.
|
||||
ColladaUtils::ImportOptions mOptions;
|
||||
|
||||
public:
|
||||
|
||||
TSShapeConstructor();
|
||||
TSShapeConstructor(const String& path) : mShapePath(path) { }
|
||||
~TSShapeConstructor();
|
||||
|
||||
DECLARE_CONOBJECT(TSShapeConstructor);
|
||||
static void initPersistFields();
|
||||
static TSShapeConstructor* findShapeConstructor(const FileName& path);
|
||||
|
||||
bool onAdd();
|
||||
|
||||
void onScriptChanged(const Torque::Path& path);
|
||||
|
||||
bool writeField(StringTableEntry fieldname, const char *value);
|
||||
void writeChangeSet();
|
||||
|
||||
void notifyShapeChanged();
|
||||
|
||||
TSShape* getShape() const { return mShape; }
|
||||
const String& getShapePath() const { return mShapePath; }
|
||||
|
||||
/// @name Dumping
|
||||
///@{
|
||||
void dumpShape( const char* filename );
|
||||
void saveShape( const char* filename );
|
||||
///@}
|
||||
|
||||
/// @name Nodes
|
||||
///@{
|
||||
S32 getNodeCount();
|
||||
S32 getNodeIndex( const char* name );
|
||||
const char* getNodeName( S32 index );
|
||||
const char* getNodeParentName( const char* name );
|
||||
bool setNodeParent( const char* name, const char* parentName );
|
||||
S32 getNodeChildCount( const char* name );
|
||||
const char* getNodeChildName( const char* name, S32 index );
|
||||
S32 getNodeObjectCount( const char* name );
|
||||
const char* getNodeObjectName( const char* name, S32 index );
|
||||
TransformF getNodeTransform( const char* name, bool isWorld=false );
|
||||
bool setNodeTransform( const char* name, TransformF txfm, bool isWorld=false );
|
||||
bool renameNode( const char* oldName, const char* newName );
|
||||
bool addNode( const char* name, const char* parentName, TransformF txfm=TransformF::Identity, bool isWorld=false);
|
||||
bool removeNode( const char* name );
|
||||
///@}
|
||||
|
||||
/// @name Materials
|
||||
///@{
|
||||
S32 getTargetCount();
|
||||
const char* getTargetName( S32 index );
|
||||
///@}
|
||||
|
||||
///@{
|
||||
S32 getObjectCount();
|
||||
const char* getObjectName( S32 index );
|
||||
S32 getObjectIndex( const char* name );
|
||||
const char* getObjectNode( const char* name );
|
||||
bool setObjectNode( const char* objName, const char* nodeName );
|
||||
bool renameObject( const char* oldName, const char* newName );
|
||||
bool removeObject( const char* name );
|
||||
///@}
|
||||
|
||||
/// @name Meshes
|
||||
///@{
|
||||
S32 getMeshCount( const char* name );
|
||||
const char* getMeshName( const char* name, S32 index );
|
||||
S32 getMeshSize( const char* name, S32 index );
|
||||
bool setMeshSize( const char* name, S32 size );
|
||||
const char* getMeshType( const char* name );
|
||||
bool setMeshType( const char* name, const char* type );
|
||||
const char* getMeshMaterial( const char* name );
|
||||
bool setMeshMaterial( const char* meshName, const char* matName );
|
||||
bool addMesh( const char* meshName, const char* srcShape, const char* srcMesh );
|
||||
bool addPrimitive( const char* meshName, const char* type, const char* params, TransformF txfm, const char* nodeName );
|
||||
bool removeMesh( const char* name );
|
||||
///@}
|
||||
|
||||
/// @name Detail Levels
|
||||
///@{
|
||||
Box3F getBounds();
|
||||
bool setBounds( Box3F bbox );
|
||||
S32 getDetailLevelCount();
|
||||
const char* getDetailLevelName( S32 index );
|
||||
S32 getDetailLevelSize( S32 index);
|
||||
S32 getDetailLevelIndex( S32 size );
|
||||
bool renameDetailLevel( const char* oldName, const char* newName );
|
||||
bool removeDetailLevel( S32 index );
|
||||
S32 setDetailLevelSize( S32 index, S32 newSize );
|
||||
S32 getImposterDetailLevel();
|
||||
const char* getImposterSettings( S32 index );
|
||||
S32 addImposter( S32 size, S32 equatorSteps, S32 polarSteps, S32 dl, S32 dim, bool includePoles, F32 polarAngle );
|
||||
bool removeImposter();
|
||||
bool addCollisionDetail( S32 size, const char* type, const char* target, S32 depth=4, F32 merge=30.0f, F32 concavity=30.0f, S32 maxVerts=32, F32 boxMaxError=0, F32 sphereMaxError=0, F32 capsuleMaxError=0 );
|
||||
///@}
|
||||
|
||||
/// @name Sequences
|
||||
///@{
|
||||
S32 getSequenceCount();
|
||||
S32 getSequenceIndex( const char* name);
|
||||
const char* getSequenceName( S32 index );
|
||||
const char* getSequenceSource( const char* name );
|
||||
S32 getSequenceFrameCount( const char* name );
|
||||
F32 getSequencePriority( const char* name );
|
||||
bool setSequencePriority( const char* name, F32 priority );
|
||||
const char* getSequenceGroundSpeed( const char* name );
|
||||
bool setSequenceGroundSpeed( const char* name, Point3F transSpeed, Point3F rotSpeed=Point3F::Zero );
|
||||
bool getSequenceCyclic( const char* name );
|
||||
bool setSequenceCyclic( const char* name, bool cyclic );
|
||||
const char* getSequenceBlend( const char* name );
|
||||
bool setSequenceBlend( const char* name, bool blend, const char* blendSeq, S32 blendFrame );
|
||||
bool renameSequence( const char* oldName, const char* newName );
|
||||
bool addSequence( const char* source, const char* name, S32 start=0, S32 end=-1, bool padRot=true, bool padTrans=false );
|
||||
bool removeSequence( const char* name );
|
||||
///@}
|
||||
|
||||
/// @name Triggers
|
||||
///@{
|
||||
S32 getTriggerCount( const char* name );
|
||||
const char* getTrigger( const char* name, S32 index );
|
||||
bool addTrigger( const char* name, S32 keyframe, S32 state );
|
||||
bool removeTrigger( const char* name, S32 keyframe, S32 state );
|
||||
///@}
|
||||
};
|
||||
|
||||
typedef domUpAxisType TSShapeConstructorUpAxis;
|
||||
typedef ColladaUtils::ImportOptions::eLodType TSShapeConstructorLodType;
|
||||
|
||||
DefineEnumType( TSShapeConstructorUpAxis );
|
||||
DefineEnumType( TSShapeConstructorLodType );
|
||||
|
||||
|
||||
/* This macro simplifies the definition of a TSShapeConstructor API method. It
|
||||
wraps the actual EngineMethod definition and automatically calls the real
|
||||
class method. It also creates a ChangeSet::Comand (with all arguments stored
|
||||
as strings). The one drawback is that it includes the open brace for the real
|
||||
class method, so to keep the code looking mostly normal, such methods start
|
||||
with another open brace, and end with a double closing brace. Not perfect,
|
||||
but a lot better than having to type out the argument list multiple times for
|
||||
the 50 odd API functions. */
|
||||
#define DefineTSShapeConstructorMethod( name, retType, args, defArgs, rawArgs, defRet, usage ) \
|
||||
DefineEngineMethod( TSShapeConstructor, name, retType, args, defArgs, usage ) \
|
||||
{ \
|
||||
/* Check that shape is loaded */ \
|
||||
if( !object->getShape() ) \
|
||||
{ \
|
||||
Con::errorf( "TSShapeConstructor::" #name " - shape not loaded" ); \
|
||||
return defRet; \
|
||||
} \
|
||||
return object->name rawArgs ; \
|
||||
} \
|
||||
/* Define the real TSShapeConstructor method */ \
|
||||
retType TSShapeConstructor::name args \
|
||||
{ \
|
||||
/* Initialise change set command (may or may not be added) */ \
|
||||
TSShapeConstructor::ChangeSet::Command newCmd( #name ); \
|
||||
newCmd.addArgs rawArgs ; \
|
||||
TORQUE_UNUSED(newCmd);
|
||||
|
||||
|
||||
/* This macro just hides the name of the auto-created ChangeSet::Command from
|
||||
above, so we are free to change the implementation later if needed */
|
||||
#define ADD_TO_CHANGE_SET() mChangeSet.add( newCmd );
|
||||
|
||||
|
||||
#endif
|
||||
2133
Engine/source/ts/tsShapeEdit.cpp
Normal file
2133
Engine/source/ts/tsShapeEdit.cpp
Normal file
File diff suppressed because it is too large
Load diff
778
Engine/source/ts/tsShapeInstance.cpp
Normal file
778
Engine/source/ts/tsShapeInstance.cpp
Normal file
|
|
@ -0,0 +1,778 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/tsShapeInstance.h"
|
||||
|
||||
#include "ts/tsLastDetail.h"
|
||||
#include "ts/tsMaterialList.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "ts/tsDecal.h"
|
||||
#include "platform/profiler.h"
|
||||
#include "core/frameAllocator.h"
|
||||
#include "gfx/gfxDevice.h"
|
||||
#include "materials/materialManager.h"
|
||||
#include "materials/materialFeatureTypes.h"
|
||||
#include "materials/sceneData.h"
|
||||
#include "materials/matInstance.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "gfx/primBuilder.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "core/module.h"
|
||||
|
||||
|
||||
MODULE_BEGIN( TSShapeInstance )
|
||||
|
||||
MODULE_INIT
|
||||
{
|
||||
Con::addVariable("$pref::TS::detailAdjust", TypeF32, &TSShapeInstance::smDetailAdjust,
|
||||
"@brief User perference for scaling the TSShape level of detail.\n"
|
||||
"The smaller the value the closer the camera must get to see the "
|
||||
"highest LOD. This setting can have a huge impact on performance in "
|
||||
"mesh heavy scenes. The default value is 1.\n"
|
||||
"@ingroup Rendering\n" );
|
||||
|
||||
Con::addVariable("$pref::TS::skipLoadDLs", TypeS32, &TSShape::smNumSkipLoadDetails,
|
||||
"@brief User perference which causes TSShapes to skip loading higher lods.\n"
|
||||
"This potentialy reduces the GPU resources and materials generated as well as "
|
||||
"limits the LODs rendered. The default value is 0.\n"
|
||||
"@see $pref::TS::skipRenderDLs\n"
|
||||
"@ingroup Rendering\n" );
|
||||
|
||||
Con::addVariable("$pref::TS::skipRenderDLs", TypeS32, &TSShapeInstance::smNumSkipRenderDetails,
|
||||
"@brief User perference which causes TSShapes to skip rendering higher lods.\n"
|
||||
"This will reduce the number of draw calls and triangles rendered and improve "
|
||||
"rendering performance when proper LODs have been created for your models. "
|
||||
"The default value is 0.\n"
|
||||
"@see $pref::TS::skipLoadDLs\n"
|
||||
"@ingroup Rendering\n" );
|
||||
|
||||
Con::addVariable("$pref::TS::smallestVisiblePixelSize", TypeF32, &TSShapeInstance::smSmallestVisiblePixelSize,
|
||||
"@brief User perference which sets the smallest pixel size at which TSShapes will skip rendering.\n"
|
||||
"This will force all shapes to stop rendering when they get smaller than this size. "
|
||||
"The default value is -1 which disables it.\n"
|
||||
"@ingroup Rendering\n" );
|
||||
|
||||
Con::addVariable("$pref::TS::maxInstancingVerts", TypeS32, &TSMesh::smMaxInstancingVerts,
|
||||
"@brief Enables mesh instancing on non-skin meshes that have less that this count of verts.\n"
|
||||
"The default value is 200. Higher values can degrade performance.\n"
|
||||
"@ingroup Rendering\n" );
|
||||
}
|
||||
|
||||
MODULE_END;
|
||||
|
||||
|
||||
F32 TSShapeInstance::smDetailAdjust = 1.0f;
|
||||
F32 TSShapeInstance::smSmallestVisiblePixelSize = -1.0f;
|
||||
S32 TSShapeInstance::smNumSkipRenderDetails = 0;
|
||||
|
||||
F32 TSShapeInstance::smLastScreenErrorTolerance = 0.0f;
|
||||
F32 TSShapeInstance::smLastScaledDistance = 0.0f;
|
||||
F32 TSShapeInstance::smLastPixelSize = 0.0f;
|
||||
|
||||
Vector<QuatF> TSShapeInstance::smNodeCurrentRotations(__FILE__, __LINE__);
|
||||
Vector<Point3F> TSShapeInstance::smNodeCurrentTranslations(__FILE__, __LINE__);
|
||||
Vector<F32> TSShapeInstance::smNodeCurrentUniformScales(__FILE__, __LINE__);
|
||||
Vector<Point3F> TSShapeInstance::smNodeCurrentAlignedScales(__FILE__, __LINE__);
|
||||
Vector<TSScale> TSShapeInstance::smNodeCurrentArbitraryScales(__FILE__, __LINE__);
|
||||
Vector<MatrixF> TSShapeInstance::smNodeLocalTransforms(__FILE__, __LINE__);
|
||||
TSIntegerSet TSShapeInstance::smNodeLocalTransformDirty;
|
||||
|
||||
Vector<TSThread*> TSShapeInstance::smRotationThreads(__FILE__, __LINE__);
|
||||
Vector<TSThread*> TSShapeInstance::smTranslationThreads(__FILE__, __LINE__);
|
||||
Vector<TSThread*> TSShapeInstance::smScaleThreads(__FILE__, __LINE__);
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// constructors, destructors, initialization
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
TSShapeInstance::TSShapeInstance( const Resource<TSShape> &shape, bool loadMaterials )
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mMeshObjects);
|
||||
VECTOR_SET_ASSOCIATION(mNodeTransforms);
|
||||
VECTOR_SET_ASSOCIATION(mNodeReferenceRotations);
|
||||
VECTOR_SET_ASSOCIATION(mNodeReferenceTranslations);
|
||||
VECTOR_SET_ASSOCIATION(mNodeReferenceUniformScales);
|
||||
VECTOR_SET_ASSOCIATION(mNodeReferenceScaleFactors);
|
||||
VECTOR_SET_ASSOCIATION(mNodeReferenceArbitraryScaleRots);
|
||||
VECTOR_SET_ASSOCIATION(mThreadList);
|
||||
VECTOR_SET_ASSOCIATION(mTransitionThreads);
|
||||
|
||||
mShapeResource = shape;
|
||||
mShape = mShapeResource;
|
||||
buildInstanceData( mShape, loadMaterials );
|
||||
}
|
||||
|
||||
TSShapeInstance::TSShapeInstance( TSShape *shape, bool loadMaterials )
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mMeshObjects);
|
||||
VECTOR_SET_ASSOCIATION(mNodeTransforms);
|
||||
VECTOR_SET_ASSOCIATION(mNodeReferenceRotations);
|
||||
VECTOR_SET_ASSOCIATION(mNodeReferenceTranslations);
|
||||
VECTOR_SET_ASSOCIATION(mNodeReferenceUniformScales);
|
||||
VECTOR_SET_ASSOCIATION(mNodeReferenceScaleFactors);
|
||||
VECTOR_SET_ASSOCIATION(mNodeReferenceArbitraryScaleRots);
|
||||
VECTOR_SET_ASSOCIATION(mThreadList);
|
||||
VECTOR_SET_ASSOCIATION(mTransitionThreads);
|
||||
|
||||
mShapeResource = NULL;
|
||||
mShape = shape;
|
||||
buildInstanceData( mShape, loadMaterials );
|
||||
}
|
||||
|
||||
TSShapeInstance::~TSShapeInstance()
|
||||
{
|
||||
mMeshObjects.clear();
|
||||
|
||||
while (mThreadList.size())
|
||||
destroyThread(mThreadList.last());
|
||||
|
||||
setMaterialList(NULL);
|
||||
|
||||
delete [] mDirtyFlags;
|
||||
}
|
||||
|
||||
void TSShapeInstance::buildInstanceData(TSShape * _shape, bool loadMaterials)
|
||||
{
|
||||
mShape = _shape;
|
||||
|
||||
debrisRefCount = 0;
|
||||
|
||||
mCurrentDetailLevel = 0;
|
||||
mCurrentIntraDetailLevel = 1.0f;
|
||||
|
||||
// all triggers off at start
|
||||
mTriggerStates = 0;
|
||||
|
||||
//
|
||||
mAlphaAlways = false;
|
||||
mAlphaAlwaysValue = 1.0f;
|
||||
|
||||
// material list...
|
||||
mMaterialList = NULL;
|
||||
mOwnMaterialList = false;
|
||||
|
||||
//
|
||||
mData = 0;
|
||||
mScaleCurrentlyAnimated = false;
|
||||
|
||||
if(loadMaterials)
|
||||
setMaterialList(mShape->materialList);
|
||||
|
||||
// set up node data
|
||||
initNodeTransforms();
|
||||
|
||||
// add objects to trees
|
||||
initMeshObjects();
|
||||
|
||||
// set up subtree data
|
||||
S32 ss = mShape->subShapeFirstNode.size(); // we have this many subtrees
|
||||
mDirtyFlags = new U32[ss];
|
||||
|
||||
mGroundThread = NULL;
|
||||
mCurrentDetailLevel = 0;
|
||||
|
||||
animateSubtrees();
|
||||
|
||||
// Construct billboards if not done already
|
||||
if ( loadMaterials && mShapeResource )
|
||||
mShape->setupBillboardDetails( mShapeResource.getPath().getFullPath() );
|
||||
}
|
||||
|
||||
void TSShapeInstance::initNodeTransforms()
|
||||
{
|
||||
// set up node data
|
||||
S32 numNodes = mShape->nodes.size();
|
||||
mNodeTransforms.setSize(numNodes);
|
||||
}
|
||||
|
||||
void TSShapeInstance::initMeshObjects()
|
||||
{
|
||||
// add objects to trees
|
||||
S32 numObjects = mShape->objects.size();
|
||||
mMeshObjects.setSize(numObjects);
|
||||
for (S32 i=0; i<numObjects; i++)
|
||||
{
|
||||
const TSObject * obj = &mShape->objects[i];
|
||||
MeshObjectInstance * objInst = &mMeshObjects[i];
|
||||
|
||||
// hook up the object to it's node and transforms.
|
||||
objInst->mTransforms = &mNodeTransforms;
|
||||
objInst->nodeIndex = obj->nodeIndex;
|
||||
|
||||
// set up list of meshes
|
||||
if (obj->numMeshes)
|
||||
objInst->meshList = &mShape->meshes[obj->startMeshIndex];
|
||||
else
|
||||
objInst->meshList = NULL;
|
||||
|
||||
objInst->object = obj;
|
||||
objInst->forceHidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeInstance::setMaterialList( TSMaterialList *matList )
|
||||
{
|
||||
// get rid of old list
|
||||
if ( mOwnMaterialList )
|
||||
delete mMaterialList;
|
||||
|
||||
mMaterialList = matList;
|
||||
mOwnMaterialList = false;
|
||||
|
||||
// If the material list is already be mapped then
|
||||
// don't bother doing the initializing a second time.
|
||||
// Note: only check the last material instance as this will catch both
|
||||
// uninitialised lists, as well as initialised lists that have had new
|
||||
// materials appended
|
||||
if ( mMaterialList && !mMaterialList->getMaterialInst( mMaterialList->size()-1 ) )
|
||||
{
|
||||
mMaterialList->setTextureLookupPath( mShapeResource.getPath().getPath() );
|
||||
mMaterialList->mapMaterials();
|
||||
Material::sAllowTextureTargetAssignment = true;
|
||||
initMaterialList();
|
||||
Material::sAllowTextureTargetAssignment = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeInstance::cloneMaterialList( const FeatureSet *features )
|
||||
{
|
||||
if ( mOwnMaterialList )
|
||||
return;
|
||||
|
||||
mMaterialList = new TSMaterialList(mMaterialList);
|
||||
initMaterialList( features );
|
||||
|
||||
mOwnMaterialList = true;
|
||||
}
|
||||
|
||||
void TSShapeInstance::initMaterialList( const FeatureSet *features )
|
||||
{
|
||||
// If we don't have features then use the default.
|
||||
if ( !features )
|
||||
features = &MATMGR->getDefaultFeatures();
|
||||
|
||||
// Initialize the materials.
|
||||
mMaterialList->initMatInstances( *features, mShape->getVertexFormat() );
|
||||
|
||||
// TODO: It would be good to go thru all the meshes and
|
||||
// pre-create all the active material hooks for shadows,
|
||||
// reflections, and instancing. This would keep these
|
||||
// hiccups from happening at runtime.
|
||||
}
|
||||
|
||||
void TSShapeInstance::reSkin( String newBaseName, String oldBaseName )
|
||||
{
|
||||
if( newBaseName.isEmpty() )
|
||||
newBaseName = "base";
|
||||
if( oldBaseName.isEmpty() )
|
||||
oldBaseName = "base";
|
||||
|
||||
if ( newBaseName.equal( oldBaseName, String::NoCase ) )
|
||||
return;
|
||||
|
||||
const U32 oldBaseNameLength = oldBaseName.length();
|
||||
|
||||
// Make our own copy of the materials list from the resource if necessary
|
||||
if (ownMaterialList() == false)
|
||||
cloneMaterialList();
|
||||
|
||||
TSMaterialList* pMatList = getMaterialList();
|
||||
pMatList->setTextureLookupPath( mShapeResource.getPath().getPath() );
|
||||
|
||||
// Cycle through the materials
|
||||
const Vector<String> &materialNames = pMatList->getMaterialNameList();
|
||||
for ( S32 i = 0; i < materialNames.size(); i++ )
|
||||
{
|
||||
// Try changing base
|
||||
const String &pName = materialNames[i];
|
||||
if ( pName.compare( oldBaseName, oldBaseNameLength, String::NoCase ) == 0 )
|
||||
{
|
||||
String newName( pName );
|
||||
newName.replace( 0, oldBaseNameLength, newBaseName );
|
||||
pMatList->renameMaterial( i, newName );
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the material instances
|
||||
initMaterialList();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Render & detail selection
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void TSShapeInstance::renderDebugNormals( F32 normalScalar, S32 dl )
|
||||
{
|
||||
if ( dl < 0 )
|
||||
return;
|
||||
|
||||
AssertFatal( dl >= 0 && dl < mShape->details.size(),
|
||||
"TSShapeInstance::renderDebugNormals() - Bad detail level!" );
|
||||
|
||||
static GFXStateBlockRef sb;
|
||||
if ( sb.isNull() )
|
||||
{
|
||||
GFXStateBlockDesc desc;
|
||||
desc.setCullMode( GFXCullNone );
|
||||
desc.setZReadWrite( true );
|
||||
desc.zWriteEnable = false;
|
||||
desc.vertexColorEnable = true;
|
||||
|
||||
sb = GFX->createStateBlock( desc );
|
||||
}
|
||||
GFX->setStateBlock( sb );
|
||||
|
||||
const TSDetail *detail = &mShape->details[dl];
|
||||
const S32 ss = detail->subShapeNum;
|
||||
if ( ss < 0 )
|
||||
return;
|
||||
|
||||
const S32 start = mShape->subShapeFirstObject[ss];
|
||||
const S32 end = start + mShape->subShapeNumObjects[ss];
|
||||
|
||||
for ( S32 i = start; i < end; i++ )
|
||||
{
|
||||
MeshObjectInstance *meshObj = &mMeshObjects[i];
|
||||
if ( !meshObj )
|
||||
continue;
|
||||
|
||||
const MatrixF &meshMat = meshObj->getTransform();
|
||||
|
||||
// Then go through each TSMesh...
|
||||
U32 m = 0;
|
||||
for( TSMesh *mesh = meshObj->getMesh(m); mesh != NULL; mesh = meshObj->getMesh(m++) )
|
||||
{
|
||||
// and pull out the list of normals.
|
||||
const U32 numNrms = mesh->mNumVerts;
|
||||
PrimBuild::begin( GFXLineList, 2 * numNrms );
|
||||
for ( U32 n = 0; n < numNrms; n++ )
|
||||
{
|
||||
Point3F norm = mesh->mVertexData[n].normal();
|
||||
Point3F vert = mesh->mVertexData[n].vert();
|
||||
|
||||
meshMat.mulP( vert );
|
||||
meshMat.mulV( norm );
|
||||
|
||||
// Then render them.
|
||||
PrimBuild::color4f( mFabs( norm.x ), mFabs( norm.y ), mFabs( norm.z ), 1.0f );
|
||||
PrimBuild::vertex3fv( vert );
|
||||
PrimBuild::vertex3fv( vert + (norm * normalScalar) );
|
||||
}
|
||||
|
||||
PrimBuild::end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeInstance::renderDebugNodes()
|
||||
{
|
||||
GFXDrawUtil *drawUtil = GFX->getDrawUtil();
|
||||
ColorI color( 255, 0, 0, 255 );
|
||||
|
||||
GFXStateBlockDesc desc;
|
||||
desc.setBlend( false );
|
||||
desc.setZReadWrite( false, false );
|
||||
|
||||
for ( U32 i = 0; i < mNodeTransforms.size(); i++ )
|
||||
drawUtil->drawTransform( desc, mNodeTransforms[i], NULL, NULL );
|
||||
}
|
||||
|
||||
void TSShapeInstance::listMeshes( const String &state ) const
|
||||
{
|
||||
if ( state.equal( "All", String::NoCase ) )
|
||||
{
|
||||
for ( U32 i = 0; i < mMeshObjects.size(); i++ )
|
||||
{
|
||||
const MeshObjectInstance &mesh = mMeshObjects[i];
|
||||
Con::warnf( "meshidx %3d, %8s, %s", i, ( mesh.forceHidden ) ? "Hidden" : "Visible", mShape->getMeshName(i).c_str() );
|
||||
}
|
||||
}
|
||||
else if ( state.equal( "Hidden", String::NoCase ) )
|
||||
{
|
||||
for ( U32 i = 0; i < mMeshObjects.size(); i++ )
|
||||
{
|
||||
const MeshObjectInstance &mesh = mMeshObjects[i];
|
||||
if ( mesh.forceHidden )
|
||||
Con::warnf( "meshidx %3d, %8s, %s", i, "Visible", mShape->getMeshName(i).c_str() );
|
||||
}
|
||||
}
|
||||
else if ( state.equal( "Visible", String::NoCase ) )
|
||||
{
|
||||
for ( U32 i = 0; i < mMeshObjects.size(); i++ )
|
||||
{
|
||||
const MeshObjectInstance &mesh = mMeshObjects[i];
|
||||
if ( !mesh.forceHidden )
|
||||
Con::warnf( "meshidx %3d, %8s, %s", i, "Hidden", mShape->getMeshName(i).c_str() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Con::warnf( "TSShapeInstance::listMeshes( %s ) - only All/Hidden/Visible are valid parameters." );
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeInstance::render( const TSRenderState &rdata )
|
||||
{
|
||||
if (mCurrentDetailLevel<0)
|
||||
return;
|
||||
|
||||
PROFILE_SCOPE( TSShapeInstance_Render );
|
||||
|
||||
// alphaIn: we start to alpha-in next detail level when intraDL > 1-alphaIn-alphaOut
|
||||
// (finishing when intraDL = 1-alphaOut)
|
||||
// alphaOut: start to alpha-out this detail level when intraDL > 1-alphaOut
|
||||
// NOTE:
|
||||
// intraDL is at 1 when if shape were any closer to us we'd be at dl-1,
|
||||
// intraDL is at 0 when if shape were any farther away we'd be at dl+1
|
||||
F32 alphaOut = mShape->alphaOut[mCurrentDetailLevel];
|
||||
F32 alphaIn = mShape->alphaIn[mCurrentDetailLevel];
|
||||
F32 saveAA = mAlphaAlways ? mAlphaAlwaysValue : 1.0f;
|
||||
|
||||
/// This first case is the single detail level render.
|
||||
if ( mCurrentIntraDetailLevel > alphaIn + alphaOut )
|
||||
render( rdata, mCurrentDetailLevel, mCurrentIntraDetailLevel );
|
||||
else if ( mCurrentIntraDetailLevel > alphaOut )
|
||||
{
|
||||
// draw this detail level w/ alpha=1 and next detail level w/
|
||||
// alpha=1-(intraDl-alphaOut)/alphaIn
|
||||
|
||||
// first draw next detail level
|
||||
if ( mCurrentDetailLevel + 1 < mShape->details.size() && mShape->details[ mCurrentDetailLevel + 1 ].size > 0.0f )
|
||||
{
|
||||
setAlphaAlways( saveAA * ( alphaIn + alphaOut - mCurrentIntraDetailLevel ) / alphaIn );
|
||||
render( rdata, mCurrentDetailLevel + 1, 0.0f );
|
||||
}
|
||||
|
||||
setAlphaAlways( saveAA );
|
||||
render( rdata, mCurrentDetailLevel, mCurrentIntraDetailLevel );
|
||||
}
|
||||
else
|
||||
{
|
||||
// draw next detail level w/ alpha=1 and this detail level w/
|
||||
// alpha = 1-intraDL/alphaOut
|
||||
|
||||
// first draw next detail level
|
||||
if ( mCurrentDetailLevel + 1 < mShape->details.size() && mShape->details[ mCurrentDetailLevel + 1 ].size > 0.0f )
|
||||
render( rdata, mCurrentDetailLevel+1, 0.0f );
|
||||
|
||||
setAlphaAlways( saveAA * mCurrentIntraDetailLevel / alphaOut );
|
||||
render( rdata, mCurrentDetailLevel, mCurrentIntraDetailLevel );
|
||||
setAlphaAlways( saveAA );
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeInstance::setMeshForceHidden( const char *meshName, bool hidden )
|
||||
{
|
||||
Vector<MeshObjectInstance>::iterator iter = mMeshObjects.begin();
|
||||
for ( ; iter != mMeshObjects.end(); iter++ )
|
||||
{
|
||||
S32 nameIndex = iter->object->nameIndex;
|
||||
const char *name = mShape->names[ nameIndex ];
|
||||
|
||||
if ( dStrcmp( meshName, name ) == 0 )
|
||||
{
|
||||
iter->forceHidden = hidden;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeInstance::setMeshForceHidden( S32 meshIndex, bool hidden )
|
||||
{
|
||||
AssertFatal( meshIndex > -1 && meshIndex < mMeshObjects.size(),
|
||||
"TSShapeInstance::setMeshForceHidden - Invalid index!" );
|
||||
|
||||
mMeshObjects[meshIndex].forceHidden = hidden;
|
||||
}
|
||||
|
||||
void TSShapeInstance::render( const TSRenderState &rdata, S32 dl, F32 intraDL )
|
||||
{
|
||||
AssertFatal( dl >= 0 && dl < mShape->details.size(),"TSShapeInstance::render" );
|
||||
|
||||
S32 i;
|
||||
|
||||
const TSDetail * detail = &mShape->details[dl];
|
||||
S32 ss = detail->subShapeNum;
|
||||
S32 od = detail->objectDetailNum;
|
||||
|
||||
// if we're a billboard detail, draw it and exit
|
||||
if ( ss < 0 )
|
||||
{
|
||||
PROFILE_SCOPE( TSShapeInstance_RenderBillboards );
|
||||
|
||||
if ( !rdata.isNoRenderTranslucent() && ( TSLastDetail::smCanShadow || !rdata.getSceneState()->isShadowPass() ) )
|
||||
mShape->billboardDetails[ dl ]->render( rdata, mAlphaAlways ? mAlphaAlwaysValue : 1.0f );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// run through the meshes
|
||||
S32 start = rdata.isNoRenderNonTranslucent() ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss];
|
||||
S32 end = rdata.isNoRenderTranslucent() ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss] + mShape->subShapeNumObjects[ss];
|
||||
for (i=start; i<end; i++)
|
||||
{
|
||||
// following line is handy for debugging, to see what part of the shape that it is rendering
|
||||
// const char *name = mShape->names[ mMeshObjects[i].object->nameIndex ];
|
||||
mMeshObjects[i].render( od, mMaterialList, rdata, mAlphaAlways ? mAlphaAlwaysValue : 1.0f );
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeInstance::setCurrentDetail( S32 dl, F32 intraDL )
|
||||
{
|
||||
PROFILE_SCOPE( TSShapeInstance_setCurrentDetail );
|
||||
|
||||
mCurrentDetailLevel = mClamp( dl, -1, mShape->mSmallestVisibleDL );
|
||||
mCurrentIntraDetailLevel = intraDL > 1.0f ? 1.0f : (intraDL < 0.0f ? 0.0f : intraDL);
|
||||
|
||||
// Restrict the chosen detail level by cutoff value.
|
||||
if ( smNumSkipRenderDetails > 0 && mCurrentDetailLevel >= 0 )
|
||||
{
|
||||
S32 cutoff = getMin( smNumSkipRenderDetails, mShape->mSmallestVisibleDL );
|
||||
if ( mCurrentDetailLevel < cutoff )
|
||||
{
|
||||
mCurrentDetailLevel = cutoff;
|
||||
mCurrentIntraDetailLevel = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
S32 TSShapeInstance::setDetailFromPosAndScale( const SceneRenderState *state,
|
||||
const Point3F &pos,
|
||||
const Point3F &scale )
|
||||
{
|
||||
VectorF camVector = pos - state->getDiffuseCameraPosition();
|
||||
F32 dist = getMax( camVector.len(), 0.01f );
|
||||
F32 invScale = ( 1.0f / getMax( getMax( scale.x, scale.y ), scale.z ) );
|
||||
|
||||
return setDetailFromDistance( state, dist * invScale );
|
||||
}
|
||||
|
||||
S32 TSShapeInstance::setDetailFromDistance( const SceneRenderState *state, F32 scaledDistance )
|
||||
{
|
||||
PROFILE_SCOPE( TSShapeInstance_setDetailFromDistance );
|
||||
|
||||
// For debugging/metrics.
|
||||
smLastScaledDistance = scaledDistance;
|
||||
|
||||
// Shortcut if the distance is really close or negative.
|
||||
if ( scaledDistance <= 0.0f )
|
||||
{
|
||||
mShape->mDetailLevelLookup[0].get( mCurrentDetailLevel, mCurrentIntraDetailLevel );
|
||||
return mCurrentDetailLevel;
|
||||
}
|
||||
|
||||
// The pixel scale is used the linearly scale the lod
|
||||
// selection based on the viewport size.
|
||||
//
|
||||
// The original calculation from TGEA was...
|
||||
//
|
||||
// pixelScale = viewport.extent.x * 1.6f / 640.0f;
|
||||
//
|
||||
// Since we now work on the viewport height, assuming
|
||||
// 4:3 aspect ratio, we've changed the reference value
|
||||
// to 300 to be more compatible with legacy shapes.
|
||||
//
|
||||
const F32 pixelScale = state->getViewport().extent.y / 300.0f;
|
||||
|
||||
// This is legacy DTS support for older "multires" based
|
||||
// meshes. The original crossbow weapon uses this.
|
||||
//
|
||||
// If we have more than one detail level and the maxError
|
||||
// is non-negative then we do some sort of screen error
|
||||
// metric for detail selection.
|
||||
//
|
||||
if ( mShape->mUseDetailFromScreenError )
|
||||
{
|
||||
// The pixel size of 1 meter at the input distance.
|
||||
F32 pixelRadius = state->projectRadius( scaledDistance, 1.0f ) * pixelScale;
|
||||
static const F32 smScreenError = 5.0f;
|
||||
return setDetailFromScreenError( smScreenError / pixelRadius );
|
||||
}
|
||||
|
||||
// We're inlining SceneRenderState::projectRadius here to
|
||||
// skip the unnessasary divide by zero protection.
|
||||
F32 pixelRadius = ( mShape->radius / scaledDistance ) * state->getWorldToScreenScale().y * pixelScale;
|
||||
F32 pixelSize = pixelRadius * smDetailAdjust;
|
||||
|
||||
if ( pixelSize > smSmallestVisiblePixelSize &&
|
||||
pixelSize <= mShape->mSmallestVisibleSize )
|
||||
pixelSize = mShape->mSmallestVisibleSize + 0.01f;
|
||||
|
||||
// For debugging/metrics.
|
||||
smLastPixelSize = pixelSize;
|
||||
|
||||
// Clamp it to an acceptable range for the lookup table.
|
||||
U32 index = (U32)mClampF( pixelSize, 0, mShape->mDetailLevelLookup.size() - 1 );
|
||||
|
||||
// Check the lookup table for the detail and intra detail levels.
|
||||
mShape->mDetailLevelLookup[ index ].get( mCurrentDetailLevel, mCurrentIntraDetailLevel );
|
||||
|
||||
// Restrict the chosen detail level by cutoff value.
|
||||
if ( smNumSkipRenderDetails > 0 && mCurrentDetailLevel >= 0 )
|
||||
{
|
||||
S32 cutoff = getMin( smNumSkipRenderDetails, mShape->mSmallestVisibleDL );
|
||||
if ( mCurrentDetailLevel < cutoff )
|
||||
{
|
||||
mCurrentDetailLevel = cutoff;
|
||||
mCurrentIntraDetailLevel = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return mCurrentDetailLevel;
|
||||
}
|
||||
|
||||
S32 TSShapeInstance::setDetailFromScreenError( F32 errorTolerance )
|
||||
{
|
||||
PROFILE_SCOPE( TSShapeInstance_setDetailFromScreenError );
|
||||
|
||||
// For debugging/metrics.
|
||||
smLastScreenErrorTolerance = errorTolerance;
|
||||
|
||||
// note: we use 10 time the average error as the metric...this is
|
||||
// more robust than the maxError...the factor of 10 is to put average error
|
||||
// on about the same scale as maxError. The errorTOL is how much
|
||||
// error we are able to tolerate before going to a more detailed version of the
|
||||
// shape. We look for a pair of details with errors bounding our errorTOL,
|
||||
// and then we select an interpolation parameter to tween betwen them. Ok, so
|
||||
// this isn't exactly an error tolerance. A tween value of 0 is the lower poly
|
||||
// model (higher detail number) and a value of 1 is the higher poly model (lower
|
||||
// detail number).
|
||||
|
||||
// deal with degenerate case first...
|
||||
// if smallest detail corresponds to less than half tolerable error, then don't even draw
|
||||
F32 prevErr;
|
||||
if ( mShape->mSmallestVisibleDL < 0 )
|
||||
prevErr = 0.0f;
|
||||
else
|
||||
prevErr = 10.0f * mShape->details[mShape->mSmallestVisibleDL].averageError * 20.0f;
|
||||
if ( mShape->mSmallestVisibleDL < 0 || prevErr < errorTolerance )
|
||||
{
|
||||
// draw last detail
|
||||
mCurrentDetailLevel = mShape->mSmallestVisibleDL;
|
||||
mCurrentIntraDetailLevel = 0.0f;
|
||||
return mCurrentDetailLevel;
|
||||
}
|
||||
|
||||
// this function is a little odd
|
||||
// the reason is that the detail numbers correspond to
|
||||
// when we stop using a given detail level...
|
||||
// we search the details from most error to least error
|
||||
// until we fit under the tolerance (errorTOL) and then
|
||||
// we use the next highest detail (higher error)
|
||||
for (S32 i = mShape->mSmallestVisibleDL; i >= 0; i-- )
|
||||
{
|
||||
F32 err0 = 10.0f * mShape->details[i].averageError;
|
||||
if ( err0 < errorTolerance )
|
||||
{
|
||||
// ok, stop here
|
||||
|
||||
// intraDL = 1 corresponds to fully this detail
|
||||
// intraDL = 0 corresponds to the next lower (higher number) detail
|
||||
mCurrentDetailLevel = i;
|
||||
mCurrentIntraDetailLevel = 1.0f - (errorTolerance - err0) / (prevErr - err0);
|
||||
return mCurrentDetailLevel;
|
||||
}
|
||||
prevErr = err0;
|
||||
}
|
||||
|
||||
// get here if we are drawing at DL==0
|
||||
mCurrentDetailLevel = 0;
|
||||
mCurrentIntraDetailLevel = 1.0f;
|
||||
return mCurrentDetailLevel;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Object (MeshObjectInstance & PluginObjectInstance) render methods
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void TSShapeInstance::ObjectInstance::render( S32, TSMaterialList *, const TSRenderState &rdata, F32 alpha )
|
||||
{
|
||||
AssertFatal(0,"TSShapeInstance::ObjectInstance::render: no default render method.");
|
||||
}
|
||||
|
||||
void TSShapeInstance::MeshObjectInstance::render( S32 objectDetail,
|
||||
TSMaterialList *materials,
|
||||
const TSRenderState &rdata,
|
||||
F32 alpha )
|
||||
{
|
||||
PROFILE_SCOPE( TSShapeInstance_MeshObjectInstance_render );
|
||||
|
||||
if ( forceHidden || ( ( visible * alpha ) <= 0.01f ) )
|
||||
return;
|
||||
|
||||
TSMesh *mesh = getMesh(objectDetail);
|
||||
if ( !mesh )
|
||||
return;
|
||||
|
||||
const MatrixF &transform = getTransform();
|
||||
|
||||
if ( rdata.getCuller() )
|
||||
{
|
||||
Box3F box( mesh->getBounds() );
|
||||
transform.mul( box );
|
||||
if ( rdata.getCuller()->isCulled( box ) )
|
||||
return;
|
||||
}
|
||||
|
||||
GFX->pushWorldMatrix();
|
||||
GFX->multWorld( transform );
|
||||
|
||||
mesh->setFade( visible * alpha );
|
||||
|
||||
// Pass a hint to the mesh that time has advanced and that the
|
||||
// skin is dirty and needs to be updated. This should result
|
||||
// in the skin only updating once per frame in most cases.
|
||||
const U32 currTime = Sim::getCurrentTime();
|
||||
bool isSkinDirty = currTime != mLastTime;
|
||||
|
||||
mesh->render( materials,
|
||||
rdata,
|
||||
isSkinDirty,
|
||||
*mTransforms,
|
||||
mVertexBuffer,
|
||||
mPrimitiveBuffer );
|
||||
|
||||
// Update the last render time.
|
||||
mLastTime = currTime;
|
||||
|
||||
GFX->popWorldMatrix();
|
||||
}
|
||||
|
||||
TSShapeInstance::MeshObjectInstance::MeshObjectInstance()
|
||||
: meshList(0), object(0), frame(0), matFrame(0),
|
||||
visible(1.0f), forceHidden(false), mLastTime( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
void TSShapeInstance::prepCollision()
|
||||
{
|
||||
PROFILE_SCOPE( TSShapeInstance_PrepCollision );
|
||||
|
||||
// Iterate over all our meshes and call prepCollision on them...
|
||||
for(S32 i=0; i<mShape->meshes.size(); i++)
|
||||
{
|
||||
if(mShape->meshes[i])
|
||||
mShape->meshes[i]->prepOpcodeCollision();
|
||||
}
|
||||
}
|
||||
|
||||
795
Engine/source/ts/tsShapeInstance.h
Normal file
795
Engine/source/ts/tsShapeInstance.h
Normal file
|
|
@ -0,0 +1,795 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSSHAPEINSTANCE_H_
|
||||
#define _TSSHAPEINSTANCE_H_
|
||||
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPE_H_
|
||||
#include "ts/tsShape.h"
|
||||
#endif
|
||||
#ifndef _TSINTEGERSET_H_
|
||||
#include "ts/tsIntegerSet.h"
|
||||
#endif
|
||||
#ifndef _CONSOLE_H_
|
||||
#include "console/console.h"
|
||||
#endif
|
||||
#ifndef _GBITMAP_H_
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
#endif
|
||||
#ifndef _TSRENDERDATA_H_
|
||||
#include "ts/tsRenderState.h"
|
||||
#endif
|
||||
#ifndef _TSMATERIALLIST_H_
|
||||
#include "ts/tsMaterialList.h"
|
||||
#endif
|
||||
|
||||
class RenderItem;
|
||||
class TSThread;
|
||||
class ConvexFeature;
|
||||
class SceneRenderState;
|
||||
class FeatureSet;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Instance versions of shape objects
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
class TSCallback
|
||||
{
|
||||
public:
|
||||
virtual ~TSCallback() {}
|
||||
|
||||
virtual void setNodeTransform(TSShapeInstance * si, S32 nodeIndex, MatrixF & localTransform) = 0;
|
||||
};
|
||||
|
||||
/// An instance of a 3space shape.
|
||||
///
|
||||
/// @section TSShapeInstance_intro Introduction
|
||||
///
|
||||
/// A 3space model represents a significant amount of data. There are multiple meshes,
|
||||
/// skeleton information, as well as animation data. Some of this, like the skeletal
|
||||
/// transforms, are unique for each instance of the model (as different instances are
|
||||
/// likely to be in different states of animation), while most of it, like texturing
|
||||
/// information and vertex data, is the same amongst all instances of the shape.
|
||||
///
|
||||
/// To keep this data from being replicated for every instance of a 3shape object, Torque
|
||||
/// uses the ResManager to instantiate and track TSShape objects. TSShape handles reading
|
||||
/// and writing 3space models, as well as keeping track of static model data, as discussed
|
||||
/// above. TSShapeInstance keeps track of all instance specific data, such as the currently
|
||||
/// playing sequences or the active node transforms.
|
||||
///
|
||||
/// TSShapeInstance contains all the functionality for 3space models, while TSShape acts as
|
||||
/// a repository for common data.
|
||||
///
|
||||
/// @section TSShapeInstance_functionality What Does TSShapeInstance Do?
|
||||
///
|
||||
/// TSShapeInstance handles several areas of functionality:
|
||||
/// - Collision.
|
||||
/// - Rendering.
|
||||
/// - Animation.
|
||||
/// - Updating skeletal transforms.
|
||||
/// - Ballooning (see setShapeBalloon() and getShapeBalloon())
|
||||
///
|
||||
/// For an excellent example of how to render a TSShape in game, see TSStatic. For examples
|
||||
/// of how to procedurally animate models, look at Player::updateLookAnimation().
|
||||
class TSShapeInstance
|
||||
{
|
||||
public:
|
||||
|
||||
struct ObjectInstance;
|
||||
friend class TSThread;
|
||||
friend class TSLastDetail;
|
||||
friend class TSPartInstance;
|
||||
|
||||
/// Base class for all renderable objects, including mesh objects and decal objects.
|
||||
///
|
||||
/// An ObjectInstance points to the renderable items in the shape...
|
||||
struct ObjectInstance
|
||||
{
|
||||
virtual ~ObjectInstance() {}
|
||||
|
||||
/// this needs to be set before using an objectInstance...tells us where to
|
||||
/// look for the transforms...gets set be shape instance 'setStatics' method
|
||||
const Vector<MatrixF> *mTransforms;
|
||||
|
||||
S32 nodeIndex;
|
||||
|
||||
/// Gets the transform of this object
|
||||
inline const MatrixF& getTransform() const
|
||||
{
|
||||
return nodeIndex < 0 ? MatrixF::Identity : (*mTransforms)[ nodeIndex ];
|
||||
}
|
||||
|
||||
/// @name Render Functions
|
||||
/// @{
|
||||
|
||||
/// Render! This draws the base-textured object.
|
||||
virtual void render( S32 objectDetail, TSMaterialList *, const TSRenderState &rdata, F32 alpha );
|
||||
/// @}
|
||||
|
||||
/// @name Collision Routines
|
||||
/// @{
|
||||
|
||||
virtual bool buildPolyList( S32 objectDetail, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials );
|
||||
virtual bool getFeatures( S32 objectDetail, const MatrixF &mat, const Point3F &n, ConvexFeature *feature, U32 &surfaceKey );
|
||||
virtual void support( S32 od, const Point3F &v, F32 *currMaxDP, Point3F *currSupport );
|
||||
|
||||
virtual bool buildPolyListOpcode( S32 objectDetail, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials );
|
||||
virtual bool castRayOpcode( S32 objectDetail, const Point3F &start, const Point3F &end, RayInfo *info, TSMaterialList *materials );
|
||||
virtual bool buildConvexOpcode( const MatrixF &mat, S32 objectDetail, const Box3F &bounds, Convex *c, Convex *list );
|
||||
|
||||
/// Ray cast for collision detection
|
||||
virtual bool castRay( S32 objectDetail, const Point3F &start, const Point3F &end, RayInfo *info, TSMaterialList* materials ) = 0;
|
||||
/// @}
|
||||
};
|
||||
|
||||
/// These are set up by default based on shape data
|
||||
struct MeshObjectInstance : ObjectInstance
|
||||
{
|
||||
TSMesh * const * meshList; ///< one mesh per detail level... Null entries allowed.
|
||||
const TSObject * object;
|
||||
S32 frame;
|
||||
S32 matFrame;
|
||||
F32 visible;
|
||||
|
||||
/// If true this mesh is forced to be hidden
|
||||
/// regardless of the animation state.
|
||||
bool forceHidden;
|
||||
|
||||
TSVertexBufferHandle mVertexBuffer;
|
||||
GFXPrimitiveBufferHandle mPrimitiveBuffer;
|
||||
|
||||
/// The time at which this mesh
|
||||
/// was last rendered.
|
||||
U32 mLastTime;
|
||||
|
||||
MeshObjectInstance();
|
||||
virtual ~MeshObjectInstance() {}
|
||||
|
||||
void render( S32 objectDetail, TSMaterialList *, const TSRenderState &rdata, F32 alpha );
|
||||
|
||||
/// Gets the mesh with specified detail level
|
||||
TSMesh * getMesh(S32 num) const { return num<object->numMeshes ? *(meshList+num) : NULL; }
|
||||
|
||||
/// @name Collision Routines
|
||||
/// @{
|
||||
|
||||
bool buildPolyList( S32 objectDetail, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials );
|
||||
bool getFeatures( S32 objectDetail, const MatrixF &mat, const Point3F &n, ConvexFeature *feature, U32 &surfaceKey );
|
||||
void support( S32 od, const Point3F &v, F32 *currMaxDP, Point3F *currSupport );
|
||||
bool castRay( S32 objectDetail, const Point3F &start, const Point3F &end, RayInfo *info, TSMaterialList *materials );
|
||||
bool castRayRendered( S32 objectDetail, const Point3F &start, const Point3F &end, RayInfo *info, TSMaterialList *materials );
|
||||
|
||||
bool buildPolyListOpcode( S32 objectDetail, AbstractPolyList *polyList, const Box3F &box, TSMaterialList* materials );
|
||||
bool castRayOpcode( S32 objectDetail, const Point3F &start, const Point3F &end, RayInfo *info, TSMaterialList *materials );
|
||||
bool buildConvexOpcode( const MatrixF &mat, S32 objectDetail, const Box3F &bounds, Convex *c, Convex *list );
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
struct TSCallbackRecord
|
||||
{
|
||||
TSCallback * callback;
|
||||
S32 nodeIndex;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Lists used for storage of transforms, nodes, objects, etc...
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
|
||||
Vector<MeshObjectInstance> mMeshObjects;
|
||||
|
||||
/// storage space for node transforms
|
||||
Vector<MatrixF> mNodeTransforms;
|
||||
|
||||
/// @name Reference Transform Vectors
|
||||
/// unused until first transition
|
||||
/// @{
|
||||
Vector<Quat16> mNodeReferenceRotations;
|
||||
Vector<Point3F> mNodeReferenceTranslations;
|
||||
Vector<F32> mNodeReferenceUniformScales;
|
||||
Vector<Point3F> mNodeReferenceScaleFactors;
|
||||
Vector<Quat16> mNodeReferenceArbitraryScaleRots;
|
||||
/// @}
|
||||
|
||||
/// @name Workspace for Node Transforms
|
||||
/// @{
|
||||
static Vector<QuatF> smNodeCurrentRotations;
|
||||
static Vector<Point3F> smNodeCurrentTranslations;
|
||||
static Vector<F32> smNodeCurrentUniformScales;
|
||||
static Vector<Point3F> smNodeCurrentAlignedScales;
|
||||
static Vector<TSScale> smNodeCurrentArbitraryScales;
|
||||
static Vector<MatrixF> smNodeLocalTransforms;
|
||||
static TSIntegerSet smNodeLocalTransformDirty;
|
||||
/// @}
|
||||
|
||||
/// @name Threads
|
||||
/// keep track of who controls what on currently animating shape
|
||||
/// @{
|
||||
static Vector<TSThread*> smRotationThreads;
|
||||
static Vector<TSThread*> smTranslationThreads;
|
||||
static Vector<TSThread*> smScaleThreads;
|
||||
/// @}
|
||||
|
||||
TSMaterialList* mMaterialList; ///< by default, points to hShape material list
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Misc.
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
protected:
|
||||
|
||||
/// @name Ground Transform Data
|
||||
/// @{
|
||||
MatrixF mGroundTransform;
|
||||
TSThread * mGroundThread;
|
||||
/// @}
|
||||
|
||||
bool mScaleCurrentlyAnimated;
|
||||
|
||||
S32 mCurrentDetailLevel;
|
||||
|
||||
/// 0-1, how far along from current to next (higher) detail level...
|
||||
///
|
||||
/// 0=at this dl, 1=at higher detail level, where higher means bigger size on screen
|
||||
/// for dl=0, we use twice detail level 0's size as the size of the "next" dl
|
||||
F32 mCurrentIntraDetailLevel;
|
||||
|
||||
/// This is only valid when the instance was created from
|
||||
/// a resource. Else it is null.
|
||||
Resource<TSShape> mShapeResource;
|
||||
|
||||
/// This should always point to a valid shape and should
|
||||
/// equal mShapeResource if it was created from a resource.
|
||||
TSShape *mShape;
|
||||
|
||||
|
||||
bool mOwnMaterialList; ///< Does this own the material list pointer?
|
||||
|
||||
bool mAlphaAlways;
|
||||
F32 mAlphaAlwaysValue;
|
||||
|
||||
bool mUseOverrideTexture;
|
||||
|
||||
U32 debrisRefCount;
|
||||
|
||||
// the threads...
|
||||
Vector<TSThread*> mThreadList;
|
||||
Vector<TSThread*> mTransitionThreads;
|
||||
|
||||
/// @name Transition nodes
|
||||
/// keep track of nodes that are involved in a transition
|
||||
///
|
||||
/// @note this only tracks nodes we're transitioning from...
|
||||
/// nodes we're transitioning to are implicitly handled
|
||||
/// (i.e., we don't need to keep track of them)
|
||||
/// @{
|
||||
|
||||
TSIntegerSet mTransitionRotationNodes;
|
||||
TSIntegerSet mTransitionTranslationNodes;
|
||||
TSIntegerSet mTransitionScaleNodes;
|
||||
/// @}
|
||||
|
||||
/// keep track of nodes with animation restrictions put on them
|
||||
TSIntegerSet mMaskRotationNodes;
|
||||
TSIntegerSet mMaskPosXNodes;
|
||||
TSIntegerSet mMaskPosYNodes;
|
||||
TSIntegerSet mMaskPosZNodes;
|
||||
TSIntegerSet mDisableBlendNodes;
|
||||
TSIntegerSet mHandsOffNodes; ///< Nodes that aren't animated through threads automatically
|
||||
TSIntegerSet mCallbackNodes;
|
||||
|
||||
// node callbacks
|
||||
Vector<TSCallbackRecord> mNodeCallbacks;
|
||||
|
||||
/// state variables
|
||||
U32 mTriggerStates;
|
||||
|
||||
bool initGround();
|
||||
void addPath(TSThread * gt, F32 start, F32 end, MatrixF * mat = NULL);
|
||||
|
||||
public:
|
||||
|
||||
TSShape* getShape() const { return mShape; }
|
||||
|
||||
TSMaterialList* getMaterialList() const { return mMaterialList; }
|
||||
|
||||
/// Set the material list without taking ownership.
|
||||
/// @see cloneMaterialList
|
||||
void setMaterialList( TSMaterialList *matList );
|
||||
|
||||
/// Call this to own the material list -- i.e., we'll make a copy of the
|
||||
/// currently set material list and be responsible for deleting it. You
|
||||
/// can pass an optional feature set for initializing the cloned materials.
|
||||
void cloneMaterialList( const FeatureSet *features = NULL );
|
||||
|
||||
/// Initializes or re-initializes the material list with
|
||||
/// an optional feature set.
|
||||
void initMaterialList( const FeatureSet *features = NULL );
|
||||
|
||||
bool ownMaterialList() const { return mOwnMaterialList; }
|
||||
|
||||
/// Get the number of material targets in this shape instance
|
||||
S32 getTargetCount() const
|
||||
{
|
||||
if ( mOwnMaterialList )
|
||||
return getMaterialList()->size();
|
||||
else
|
||||
return getShape()->getTargetCount();
|
||||
}
|
||||
|
||||
/// Get the indexed material target (may differ from the base TSShape material
|
||||
/// list if this instance has been reskinned).
|
||||
const String& getTargetName( S32 mapToNameIndex ) const
|
||||
{
|
||||
if ( mOwnMaterialList )
|
||||
{
|
||||
if ( mapToNameIndex < 0 || mapToNameIndex >= getMaterialList()->size() )
|
||||
return String::EmptyString;
|
||||
|
||||
return getMaterialList()->getMaterialName( mapToNameIndex );
|
||||
}
|
||||
else
|
||||
{
|
||||
return getShape()->getTargetName( mapToNameIndex );
|
||||
}
|
||||
}
|
||||
|
||||
void reSkin( String newBaseName, String oldBaseName = String::EmptyString );
|
||||
|
||||
enum
|
||||
{
|
||||
MaskNodeRotation = 0x01,
|
||||
MaskNodePosX = 0x02,
|
||||
MaskNodePosY = 0x04,
|
||||
MaskNodePosZ = 0x08,
|
||||
MaskNodeBlend = 0x10,
|
||||
MaskNodeAll = MaskNodeRotation|MaskNodePosX|MaskNodePosY|MaskNodePosZ|MaskNodeBlend,
|
||||
MaskNodeAllButBlend = MaskNodeRotation|MaskNodePosX|MaskNodePosY|MaskNodePosZ,
|
||||
MaskNodeAllButRotation = MaskNodePosX|MaskNodePosY|MaskNodePosZ|MaskNodeBlend,
|
||||
MaskNodeAllButPosX = MaskNodeRotation|MaskNodePosY|MaskNodePosZ|MaskNodeBlend,
|
||||
MaskNodeAllButPosY = MaskNodeRotation|MaskNodePosX|MaskNodePosZ|MaskNodeBlend,
|
||||
MaskNodeAllButPosZ = MaskNodeRotation|MaskNodePosX|MaskNodePosY|MaskNodeBlend,
|
||||
MaskNodeHandsOff = 0x20, ///< meaning, don't even set to default, programmer controls it (blend still applies)
|
||||
MaskNodeCallback = 0x40 ///< meaning, get local transform via callback function (see setCallback)
|
||||
///< callback data2 is node index, callback return value is pointer to local transform
|
||||
///< Note: won't get this callback everytime you animate...application responsibility
|
||||
///< to make sure matrix pointer continues to point to valid and updated local transform
|
||||
};
|
||||
/// @name Node Masking
|
||||
/// set node masking...
|
||||
/// @{
|
||||
void setNodeAnimationState(S32 nodeIndex, U32 animationState, TSCallback * callback = NULL);
|
||||
U32 getNodeAnimationState(S32 nodeIndex);
|
||||
/// @}
|
||||
|
||||
/// @name Trigger states
|
||||
/// check trigger value
|
||||
/// @{
|
||||
bool getTriggerState(U32 stateNum, bool clearState = true);
|
||||
void setTriggerState(U32 stateNum, bool on);
|
||||
void setTriggerStateBit(U32 stateBit, bool on);
|
||||
/// @}
|
||||
|
||||
/// @name Debris Management
|
||||
/// @{
|
||||
void incDebrisRefCount() { ++debrisRefCount; }
|
||||
void decDebrisRefCount() { debrisRefCount > 0 ? --debrisRefCount : 0; }
|
||||
U32 getDebrisRefCount() const { return debrisRefCount; }
|
||||
/// @}
|
||||
|
||||
/// @name AlphaAlways
|
||||
/// AlphaAlways allows the entire model to become translucent at the same value
|
||||
/// @{
|
||||
void setAlphaAlways(F32 value) { mAlphaAlways = (value<0.99f); mAlphaAlwaysValue = value; }
|
||||
F32 getAlphaAlwaysValue() const { return mAlphaAlways ? mAlphaAlwaysValue : 1.0f; }
|
||||
bool getAlphaAlways() const { return mAlphaAlways; }
|
||||
/// @}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// private methods for setting up and affecting animation
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
|
||||
/// @name Private animation methods
|
||||
/// These are private methods for setting up and affecting animation
|
||||
/// @{
|
||||
void sortThreads();
|
||||
|
||||
void updateTransitions();
|
||||
void handleDefaultScale(S32 a, S32 b, TSIntegerSet & scaleBeenSet);
|
||||
void updateTransitionNodeTransforms(TSIntegerSet& transitionNodes);
|
||||
void handleTransitionNodes(S32 a, S32 b);
|
||||
void handleNodeScale(S32 a, S32 b);
|
||||
void handleAnimatedScale(TSThread *, S32 a, S32 b, TSIntegerSet &);
|
||||
void handleMaskedPositionNode(TSThread *, S32 nodeIndex, S32 offset);
|
||||
void handleBlendSequence(TSThread *, S32 a, S32 b);
|
||||
void checkScaleCurrentlyAnimated();
|
||||
/// @}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// animate, render, & detail control
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
|
||||
struct RenderData
|
||||
{
|
||||
MeshObjectInstance* currentObjectInstance;
|
||||
|
||||
S32 detailLevel;
|
||||
S32 materialIndex;
|
||||
const Point3F * objectScale;
|
||||
};
|
||||
|
||||
/// Scale pixel size by this amount when selecting
|
||||
/// detail levels.
|
||||
static F32 smDetailAdjust;
|
||||
|
||||
/// If this is set to a positive pixel value shapes
|
||||
/// with a smaller pixel size than this will skip
|
||||
/// rendering entirely.
|
||||
static F32 smSmallestVisiblePixelSize;
|
||||
|
||||
/// never choose detail level number below this value (except if
|
||||
/// only way to get a visible detail)
|
||||
static S32 smNumSkipRenderDetails;
|
||||
|
||||
/// For debugging / metrics.
|
||||
static F32 smLastScreenErrorTolerance;
|
||||
static F32 smLastScaledDistance;
|
||||
static F32 smLastPixelSize;
|
||||
|
||||
/// Debugging
|
||||
/// @{
|
||||
|
||||
/// Renders the vertex normals assuming the GFX state
|
||||
/// is setup for rendering in model space.
|
||||
void renderDebugNormals( F32 normalScalar, S32 dl );
|
||||
|
||||
/// Render all node transforms as small axis gizmos. It is recommended
|
||||
/// that prior to calling this, shapeInstance::animate is called so that
|
||||
/// nodes are in object space and that the GFX state is setup for
|
||||
/// rendering in model space.
|
||||
void renderDebugNodes();
|
||||
|
||||
/// Print mesh data to the console, valid String parameters
|
||||
/// are Visible, Hidden, or All.
|
||||
void listMeshes( const String &state ) const;
|
||||
|
||||
/// @}
|
||||
|
||||
virtual void render( const TSRenderState &rdata );
|
||||
virtual void render( const TSRenderState &rdata, S32 dl, F32 intraDL = 0.0f );
|
||||
|
||||
void animate() { animate( mCurrentDetailLevel ); }
|
||||
void animate(S32 dl);
|
||||
void animateNodes(S32 ss);
|
||||
void animateVisibility(S32 ss);
|
||||
void animateFrame(S32 ss);
|
||||
void animateMatFrame(S32 ss);
|
||||
void animateSubtrees(bool forceFull = true);
|
||||
void animateNodeSubtrees(bool forceFull = true);
|
||||
|
||||
/// Sets the 'forceHidden' state on the named mesh.
|
||||
/// @see MeshObjectInstance::forceHidden
|
||||
void setMeshForceHidden( const char *meshName, bool hidden );
|
||||
|
||||
/// Sets the 'forceHidden' state on a mesh.
|
||||
/// @see MeshObjectInstance::forceHidden
|
||||
void setMeshForceHidden( S32 meshIndex, bool hidden );
|
||||
|
||||
/// @name Animation Scale
|
||||
/// Query about animated scale
|
||||
/// @{
|
||||
bool animatesScale() { return (mShape->mFlags & TSShape::AnyScale) != 0; }
|
||||
bool animatesUniformScale() { return (mShape->mFlags & TSShape::UniformScale) != 0; }
|
||||
bool animatesAlignedScale() { return (mShape->mFlags & TSShape::AlignedScale) != 0; }
|
||||
bool animatesArbitraryScale() { return (mShape->mFlags & TSShape::ArbitraryScale) != 0; }
|
||||
bool scaleCurrentlyAnimated() { return mScaleCurrentlyAnimated; }
|
||||
/// @}
|
||||
|
||||
//
|
||||
bool inTransition() { return !mTransitionThreads.empty(); }
|
||||
|
||||
/// @name Ground Transforms
|
||||
/// The animator of a model can make the bounding box
|
||||
/// animate along with the object. Doing so will move the object with the bounding box.
|
||||
/// The ground transform turns the world bounding box into the post-animation bounding box
|
||||
/// when such a technique is used. However, few models actually use this technique.
|
||||
/// @{
|
||||
|
||||
void animateGround(); ///< clears previous ground transform
|
||||
MatrixF & getGroundTransform() { return mGroundTransform; }
|
||||
void deltaGround(TSThread *, F32 start, F32 end, MatrixF * mat = NULL);
|
||||
void deltaGround1(TSThread *, F32 start, F32 end, MatrixF& mat);
|
||||
/// @}
|
||||
|
||||
U32 getNumDetails() const { return mShape ? mShape->details.size() : 0; }
|
||||
|
||||
S32 getCurrentDetail() const { return mCurrentDetailLevel; }
|
||||
|
||||
F32 getCurrentIntraDetail() const { return mCurrentIntraDetailLevel; }
|
||||
|
||||
void setCurrentDetail( S32 dl, F32 intraDL = 1.0f );
|
||||
|
||||
/// Helper function which internally calls setDetailFromDistance.
|
||||
S32 setDetailFromPosAndScale( const SceneRenderState *state,
|
||||
const Point3F &pos,
|
||||
const Point3F &scale );
|
||||
|
||||
/// Selects the current detail level using the scaled
|
||||
/// distance between your object and the camera.
|
||||
///
|
||||
/// @see TSShape::Detail.
|
||||
S32 setDetailFromDistance( const SceneRenderState *state, F32 scaledDist );
|
||||
|
||||
/// Sets the current detail level using the legacy screen error metric.
|
||||
S32 setDetailFromScreenError( F32 errorTOL );
|
||||
|
||||
enum
|
||||
{
|
||||
TransformDirty = BIT(0),
|
||||
VisDirty = BIT(1),
|
||||
FrameDirty = BIT(2),
|
||||
MatFrameDirty = BIT(3),
|
||||
ThreadDirty = BIT(4),
|
||||
AllDirtyMask = TransformDirty | VisDirty | FrameDirty | MatFrameDirty | ThreadDirty
|
||||
};
|
||||
U32 * mDirtyFlags;
|
||||
void setDirty(U32 dirty);
|
||||
void clearDirty(U32 dirty);
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// collision interface routines
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
|
||||
bool buildPolyList(AbstractPolyList *, S32 dl);
|
||||
bool getFeatures(const MatrixF& mat, const Point3F& n, ConvexFeature*, S32 dl);
|
||||
bool castRay(const Point3F & start, const Point3F & end, RayInfo *,S32 dl);
|
||||
bool castRayRendered(const Point3F & start, const Point3F & end, RayInfo *,S32 dl);
|
||||
bool quickLOS(const Point3F & start, const Point3F & end, S32 dl) { return castRay(start,end,NULL,dl); }
|
||||
Point3F support(const Point3F & v, S32 dl);
|
||||
void computeBounds(S32 dl, Box3F & bounds); ///< uses current transforms to compute bounding box around a detail level
|
||||
///< see like named method on shape if you want to use default transforms
|
||||
|
||||
bool buildPolyListOpcode( S32 dl, AbstractPolyList *polyList, const Box3F &box );
|
||||
bool castRayOpcode( S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo *);
|
||||
bool buildConvexOpcode( const MatrixF &objMat, const Point3F &objScale, S32 objectDetail, const Box3F &bounds, Convex *c, Convex *list );
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Thread Control
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
/// @name Thread Control
|
||||
/// Threads! In order to animate an object, first you need to have an animation in the object.
|
||||
/// Then, you need to get the TSShape of the object:
|
||||
/// @code
|
||||
/// TSShape* shape = mShapeInstance->getShape());
|
||||
/// @endcode
|
||||
/// Next, get the sequence and store::
|
||||
/// @code
|
||||
/// S32 seq = shape->findSequence("foo"));
|
||||
/// @endcode
|
||||
/// Create a new thread (if needed):
|
||||
/// @code
|
||||
/// TSThread* thread = mShapeInstance->addThread();
|
||||
/// @endcode
|
||||
/// Finally, set the position in the sequence:
|
||||
/// @code
|
||||
/// mShapeInstance->setSequence(thread, seq, 0)
|
||||
/// @endcode
|
||||
/// @{
|
||||
|
||||
public:
|
||||
|
||||
TSThread * addThread(); ///< Create a new thread
|
||||
TSThread * getThread(S32 threadNumber); ///< @note threads can change order, best to hold
|
||||
///< onto a thread from the start
|
||||
void destroyThread(TSThread * thread); ///< Destroy a thread!
|
||||
U32 threadCount(); ///< How many threads are there?
|
||||
|
||||
void setSequence(TSThread *, S32 seq, F32 pos);///< Get the thread a sequence
|
||||
/// Transition to a sequence
|
||||
void transitionToSequence(TSThread *, S32 seq, F32 pos, F32 duration, bool continuePlay);
|
||||
void clearTransition(TSThread *); ///< Stop transitions
|
||||
U32 getSequence(TSThread *); ///< Get the sequence of the thread
|
||||
|
||||
void setBlendEnabled(TSThread *, bool blendOn);///< Set whether or not the thread will blend
|
||||
bool getBlendEnabled(TSThread *); ///< Does this thread blend?
|
||||
|
||||
void setPriority(TSThread *, F32 priority); ///< Set thread priority
|
||||
F32 getPriority(TSThread * thread); ///< Get thread priority
|
||||
|
||||
F32 getTime(TSThread * thread); ///< Get how long the thread has been playing
|
||||
F32 getPos(TSThread * thread); ///< Get the position in the thread
|
||||
|
||||
void setTime(TSThread * thread, F32 time); ///< Set how long into the thread to use
|
||||
void setPos(TSThread * thread, F32 pos); ///< Set the position of the thread
|
||||
|
||||
bool isInTransition(TSThread * thread); ///< Is this thread in transition?
|
||||
F32 getTimeScale(TSThread * thread); ///< Get the time scale of the thread
|
||||
void setTimeScale(TSThread * thread, F32); ///< Set the time scale of the thread
|
||||
|
||||
F32 getDuration(TSThread * thread); ///< Get the duration of the thread
|
||||
F32 getScaledDuration(TSThread * thread); ///< Get the duration of the thread with the scale factored in
|
||||
|
||||
S32 getKeyframeCount(TSThread * thread); ///< Get the number of keyframes
|
||||
S32 getKeyframeNumber(TSThread * thread); ///< Get which keyframe the thread is on
|
||||
/// Set which keyframe the thread is on
|
||||
void setKeyframeNumber(TSThread * thread, S32 kf);
|
||||
|
||||
void advanceTime(F32 delta, TSThread *); ///< advance time on a particular thread
|
||||
void advanceTime(F32 delta); ///< advance time on all threads
|
||||
void advancePos(F32 delta, TSThread *); ///< advance pos on a particular thread
|
||||
void advancePos(F32 delta); ///< advance pos on all threads
|
||||
/// @}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// constructors, destructors, initialization, io
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
TSShapeInstance( const Resource<TSShape> & shape, bool loadMaterials = true);
|
||||
TSShapeInstance( TSShape * pShape, bool loadMaterials = true);
|
||||
~TSShapeInstance();
|
||||
|
||||
void buildInstanceData(TSShape *, bool loadMaterials);
|
||||
void initNodeTransforms();
|
||||
void initMeshObjects();
|
||||
|
||||
void dump(Stream &);
|
||||
void dumpNode(Stream &, S32 level, S32 nodeIndex, Vector<S32> & detailSizes);
|
||||
|
||||
void *mData; ///< available for use by app...initialized to 0
|
||||
|
||||
void prepCollision();
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Thread class
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
/// 3space animation thread.
|
||||
///
|
||||
/// An animation thread: runtime data associated with a single sequence that is
|
||||
/// running (or two sequences if in transition between them).
|
||||
///
|
||||
/// A shape instance can have multiple threads running. When multiple threads are running,
|
||||
/// which thread/sequence controls which node or object is determined based
|
||||
/// on priority of the sequence.
|
||||
///
|
||||
/// @note all thread data and methods are private (but TSShapeInstance is a friend).
|
||||
/// Users should treat thread pointers like keys -- they are used to ID
|
||||
/// the thread when interfacing with the shape, but are not manipulated
|
||||
/// by anything but the TSShapeInstance. See "Thread control" methods
|
||||
/// for more info on controlling threads.
|
||||
class TSThread
|
||||
{
|
||||
friend class TSShapeInstance;
|
||||
|
||||
S32 priority;
|
||||
|
||||
TSShapeInstance * mShapeInstance; ///< Instance of the shape that this thread animates
|
||||
|
||||
S32 sequence; ///< Sequence this thread will perform
|
||||
F32 pos;
|
||||
|
||||
F32 timeScale; ///< How fast to play through the sequence
|
||||
|
||||
S32 keyNum1; ///< Keyframe at or before current position
|
||||
S32 keyNum2; ///< Keyframe at or after current position
|
||||
F32 keyPos;
|
||||
|
||||
bool blendDisabled; ///< Blend with other sequences?
|
||||
|
||||
/// if in transition...
|
||||
struct TransitionData
|
||||
{
|
||||
bool inTransition;
|
||||
|
||||
F32 duration;
|
||||
F32 pos;
|
||||
F32 direction;
|
||||
F32 targetScale; ///< time scale for sequence we are transitioning to (during transition only)
|
||||
///< this is either 1 or 0 (if 1 target sequence plays as we transition, if 0 it doesn't)
|
||||
TSIntegerSet oldRotationNodes; ///< nodes controlled by this thread pre-transition
|
||||
TSIntegerSet oldTranslationNodes; ///< nodes controlled by this thread pre-transition
|
||||
TSIntegerSet oldScaleNodes; ///< nodes controlled by this thread pre-transition
|
||||
U32 oldSequence; ///< sequence that was set before transition began
|
||||
F32 oldPos; ///< position of sequence before transition began
|
||||
} transitionData;
|
||||
|
||||
struct
|
||||
{
|
||||
F32 start;
|
||||
F32 end;
|
||||
S32 loop;
|
||||
} path;
|
||||
bool makePath;
|
||||
|
||||
/// given a position on the thread, choose correct keyframes
|
||||
/// slight difference between one-shot and cyclic sequences -- see comments below for details
|
||||
void selectKeyframes(F32 pos, const TSSequence * seq, S32 * k1, S32 * k2, F32 * kpos);
|
||||
|
||||
void getGround(F32 p, MatrixF * pMat);
|
||||
|
||||
/// @name Triggers
|
||||
/// Triggers are used to do something once a certain animation point has been reached.
|
||||
///
|
||||
/// For example, when the player's foot animation hits the ground, a foot puff and
|
||||
/// foot print are triggered from the thread.
|
||||
///
|
||||
/// These are called by advancePos()
|
||||
/// @{
|
||||
void animateTriggers();
|
||||
void activateTriggers(F32 a, F32 b);
|
||||
/// @}
|
||||
|
||||
TSThread(TSShapeInstance*);
|
||||
TSThread() {}
|
||||
|
||||
void setSequence(S32 seq, F32 pos);
|
||||
void transitionToSequence(S32 seq, F32 pos, F32 duration, bool continuePlay);
|
||||
|
||||
void advanceTime(F32 delta);
|
||||
void advancePos(F32 delta);
|
||||
|
||||
F32 getTime();
|
||||
F32 getPos();
|
||||
|
||||
void setTime(F32);
|
||||
void setPos(F32);
|
||||
|
||||
bool isInTransition();
|
||||
F32 getTimeScale();
|
||||
void setTimeScale(F32);
|
||||
|
||||
F32 getDuration();
|
||||
F32 getScaledDuration();
|
||||
|
||||
S32 getKeyframeCount();
|
||||
S32 getKeyframeNumber();
|
||||
void setKeyframeNumber(S32 kf);
|
||||
|
||||
public:
|
||||
|
||||
TSShapeInstance * getShapeInstance() { return mShapeInstance; }
|
||||
bool hasSequence() const { return sequence >= 0; }
|
||||
U32 getSeqIndex() const { return sequence; }
|
||||
const TSSequence* getSequence() const { return &(mShapeInstance->mShape->sequences[sequence]); }
|
||||
const String& getSequenceName() const { return mShapeInstance->mShape->getSequenceName(sequence); }
|
||||
S32 operator<(const TSThread &) const;
|
||||
};
|
||||
|
||||
typedef TSShapeInstance::ObjectInstance TSObjectInstance;
|
||||
|
||||
#endif // _TSSHAPEINSTANCE_H_
|
||||
886
Engine/source/ts/tsShapeOldRead.cpp
Normal file
886
Engine/source/ts/tsShapeOldRead.cpp
Normal file
|
|
@ -0,0 +1,886 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "core/strings/stringFunctions.h"
|
||||
#include "core/util/endian.h"
|
||||
|
||||
#include "ts/tsShapeInstance.h"
|
||||
|
||||
//-------------------------------------------------
|
||||
// put old skins into object list
|
||||
//-------------------------------------------------
|
||||
|
||||
void TSShape::fixupOldSkins(S32 numMeshes, S32 numSkins, S32 numDetails, S32 * detailFirstSkin, S32 * detailNumSkins)
|
||||
{
|
||||
#if !defined(TORQUE_MAX_LIB)
|
||||
// this method not necessary in exporter, and a couple lines won't compile for exporter
|
||||
if (!objects.address() || !meshes.address() || !numSkins)
|
||||
// not ready for this yet, will catch it on the next pass
|
||||
return;
|
||||
S32 numObjects = objects.size();
|
||||
TSObject * newObjects = objects.address() + objects.size();
|
||||
TSSkinMesh ** skins = (TSSkinMesh**)&meshes[numMeshes];
|
||||
Vector<TSSkinMesh*> skinsCopy;
|
||||
// Note: newObjects has as much free space as we need, so we just need to keep track of the
|
||||
// number of objects we use and then update objects.size
|
||||
S32 numSkinObjects = 0;
|
||||
S32 skinsUsed = 0;
|
||||
S32 emptySkins = 0;
|
||||
S32 i;
|
||||
for (i=0; i<numSkins; i++)
|
||||
if (skins[i]==NULL)
|
||||
emptySkins++; // probably never, but just in case
|
||||
while (skinsUsed<numSkins-emptySkins)
|
||||
{
|
||||
TSObject & object = newObjects[numSkinObjects++];
|
||||
objects.increment();
|
||||
object.nameIndex = 0; // no name
|
||||
object.numMeshes = 0;
|
||||
object.startMeshIndex = numMeshes + skinsCopy.size();
|
||||
object.nodeIndex = -1;
|
||||
object.nextSibling = -1;
|
||||
for (S32 dl=0; dl<numDetails; dl++)
|
||||
{
|
||||
// find one mesh per detail to add to this object
|
||||
// don't really need to be versions of the same object
|
||||
i = 0;
|
||||
while (i<detailFirstSkin[dl] || detailFirstSkin[dl]<0)
|
||||
i++;
|
||||
for (; i<numSkins && i<detailFirstSkin[dl]+detailNumSkins[dl]; i++)
|
||||
{
|
||||
if (skins[i])
|
||||
{
|
||||
// found an unused skin... copy it to skinsCopy and set to NULL
|
||||
skinsCopy.push_back(skins[i]);
|
||||
skins[i]=NULL;
|
||||
object.numMeshes++;
|
||||
skinsUsed++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i==numSkins || i==detailFirstSkin[dl]+detailNumSkins[dl])
|
||||
{
|
||||
skinsCopy.push_back(NULL);
|
||||
object.numMeshes++;
|
||||
}
|
||||
}
|
||||
// exit above loop with one skin per detail...despose of trailing null meshes
|
||||
while (!skinsCopy.empty() && skinsCopy.last()==NULL)
|
||||
{
|
||||
skinsCopy.decrement();
|
||||
object.numMeshes--;
|
||||
}
|
||||
// if no meshes, don't need object
|
||||
if (!object.numMeshes)
|
||||
{
|
||||
objects.decrement();
|
||||
numSkinObjects--;
|
||||
}
|
||||
}
|
||||
dMemcpy(skins,skinsCopy.address(),skinsCopy.size()*sizeof(TSSkinMesh*));
|
||||
|
||||
if (subShapeFirstObject.size()==1)
|
||||
// as long as only one subshape, we'll now be rendered
|
||||
subShapeNumObjects[0] += numSkinObjects;
|
||||
|
||||
// now for something ugly -- we've added somoe objects to hold the skins...
|
||||
// now we have to add default states for those objects
|
||||
// we also have to increment base states on all the sequences that are loaded
|
||||
dMemmove(objectStates.address()+numObjects+numSkinObjects,objectStates.address()+numObjects,(objectStates.size()-numObjects)*sizeof(ObjectState));
|
||||
for (i=numObjects; i<numObjects+numSkinObjects; i++)
|
||||
{
|
||||
objectStates[i].vis=1.0f;
|
||||
objectStates[i].frameIndex=0;
|
||||
objectStates[i].matFrameIndex=0;
|
||||
}
|
||||
for (i=0;i<sequences.size();i++)
|
||||
{
|
||||
sequences[i].baseObjectState += numSkinObjects;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// some macros used for read/write
|
||||
//-------------------------------------------------
|
||||
|
||||
// write a vector of structs (minus the first 'm')
|
||||
#define writeVectorStructMinus(a,m) \
|
||||
{\
|
||||
s->write(a.size() - m); \
|
||||
for (S32 i=m;i<a.size();i++) \
|
||||
a[i].write(s); \
|
||||
}
|
||||
|
||||
// write a vector of simple types (minus the first 'm')
|
||||
#define writeVectorSimpleMinus(a,m) \
|
||||
{\
|
||||
s->write(a.size() - m); \
|
||||
for (S32 i=m;i<a.size();i++) \
|
||||
s->write(a[i]); \
|
||||
}
|
||||
|
||||
// same as above with m=0
|
||||
#define writeVectorStruct(a) writeVectorStructMinus(a,0)
|
||||
#define writeVectorSimple(a) writeVectorSimpleMinus(a,0)
|
||||
|
||||
// read a vector of structs -- over-writing any existing data
|
||||
#define readVectorStruct(a) \
|
||||
{ \
|
||||
S32 sz; \
|
||||
s->read(&sz); \
|
||||
a.setSize(sz); \
|
||||
for (S32 i=0;i<sz;i++) \
|
||||
a[i].read(s); \
|
||||
}
|
||||
|
||||
// read a vector of simple types -- over-writing any existing data
|
||||
#define readVectorSimple(a) \
|
||||
{ \
|
||||
S32 sz; \
|
||||
s->read(&sz); \
|
||||
a.setSize(sz); \
|
||||
for (S32 i=0;i<sz;i++) \
|
||||
s->read(&a[i]); \
|
||||
}
|
||||
|
||||
// read a vector of structs -- append to any existing data
|
||||
#define appendVectorStruct(a) \
|
||||
{ \
|
||||
S32 sz; \
|
||||
S32 oldSz = a.size(); \
|
||||
s->read(&sz); \
|
||||
a.setSize(oldSz + sz); \
|
||||
for (S32 i=0;i<sz;i++) \
|
||||
a[i + oldSz].read(s); \
|
||||
}
|
||||
|
||||
// read a vector of simple types -- append to any existing data
|
||||
#define appendVectorSimple(a) \
|
||||
{ \
|
||||
S32 sz; \
|
||||
S32 oldSz = a.size(); \
|
||||
s->read(&sz); \
|
||||
a.setSize(oldSz + sz); \
|
||||
for (S32 i=0;i<sz;i++) \
|
||||
s->read(&a[i + oldSz]); \
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// export all sequences
|
||||
//-------------------------------------------------
|
||||
void TSShape::exportSequences(Stream * s)
|
||||
{
|
||||
// write version
|
||||
s->write(smVersion);
|
||||
|
||||
S32 i,sz;
|
||||
|
||||
// write node names
|
||||
// -- this is how we will map imported sequence nodes to shape nodes
|
||||
sz = nodes.size();
|
||||
s->write(sz);
|
||||
for (i=0;i<nodes.size();i++)
|
||||
writeName(s,nodes[i].nameIndex);
|
||||
|
||||
// legacy write -- write zero objects, don't pretend to support object export anymore
|
||||
s->write(0);
|
||||
|
||||
// on import, we will need to adjust keyframe data based on number of
|
||||
// nodes/objects in this shape...number of nodes can be inferred from
|
||||
// above, but number of objects cannot be. Write that quantity here:
|
||||
s->write(objects.size());
|
||||
|
||||
// write node states -- skip default node states
|
||||
s->write(nodeRotations.size());
|
||||
for (i=0;i<nodeRotations.size();i++)
|
||||
{
|
||||
s->write(nodeRotations[i].x);
|
||||
s->write(nodeRotations[i].y);
|
||||
s->write(nodeRotations[i].z);
|
||||
s->write(nodeRotations[i].w);
|
||||
}
|
||||
s->write(nodeTranslations.size());
|
||||
for (i=0;i<nodeTranslations.size(); i++)
|
||||
{
|
||||
s->write(nodeTranslations[i].x);
|
||||
s->write(nodeTranslations[i].y);
|
||||
s->write(nodeTranslations[i].z);
|
||||
}
|
||||
s->write(nodeUniformScales.size());
|
||||
for (i=0;i<nodeUniformScales.size();i++)
|
||||
s->write(nodeUniformScales[i]);
|
||||
s->write(nodeAlignedScales.size());
|
||||
for (i=0;i<nodeAlignedScales.size();i++)
|
||||
{
|
||||
s->write(nodeAlignedScales[i].x);
|
||||
s->write(nodeAlignedScales[i].y);
|
||||
s->write(nodeAlignedScales[i].z);
|
||||
}
|
||||
s->write(nodeArbitraryScaleRots.size());
|
||||
for (i=0;i<nodeArbitraryScaleRots.size();i++)
|
||||
{
|
||||
s->write(nodeArbitraryScaleRots[i].x);
|
||||
s->write(nodeArbitraryScaleRots[i].y);
|
||||
s->write(nodeArbitraryScaleRots[i].z);
|
||||
s->write(nodeArbitraryScaleRots[i].w);
|
||||
}
|
||||
for (i=0;i<nodeArbitraryScaleFactors.size();i++)
|
||||
{
|
||||
s->write(nodeArbitraryScaleFactors[i].x);
|
||||
s->write(nodeArbitraryScaleFactors[i].y);
|
||||
s->write(nodeArbitraryScaleFactors[i].z);
|
||||
}
|
||||
s->write(groundTranslations.size());
|
||||
for (i=0;i<groundTranslations.size();i++)
|
||||
{
|
||||
s->write(groundTranslations[i].x);
|
||||
s->write(groundTranslations[i].y);
|
||||
s->write(groundTranslations[i].z);
|
||||
}
|
||||
for (i=0;i<groundRotations.size();i++)
|
||||
{
|
||||
s->write(groundRotations[i].x);
|
||||
s->write(groundRotations[i].y);
|
||||
s->write(groundRotations[i].z);
|
||||
s->write(groundRotations[i].w);
|
||||
}
|
||||
|
||||
// write object states -- legacy..no object states
|
||||
s->write((S32)0);
|
||||
|
||||
// write sequences
|
||||
s->write(sequences.size());
|
||||
for (i=0;i<sequences.size();i++)
|
||||
{
|
||||
Sequence & seq = sequences[i];
|
||||
|
||||
// first write sequence name
|
||||
writeName(s,seq.nameIndex);
|
||||
|
||||
// now write the sequence itself
|
||||
seq.write(s,false); // false --> don't write name index
|
||||
}
|
||||
|
||||
// write out all the triggers...
|
||||
s->write(triggers.size());
|
||||
for (i=0; i<triggers.size(); i++)
|
||||
{
|
||||
s->write(triggers[i].state);
|
||||
s->write(triggers[i].pos);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// export a single sequence
|
||||
//-------------------------------------------------
|
||||
void TSShape::exportSequence(Stream * s, const TSShape::Sequence& seq, bool saveOldFormat)
|
||||
{
|
||||
S32 currentVersion = smVersion;
|
||||
if ( saveOldFormat )
|
||||
smVersion = 24;
|
||||
|
||||
// write version
|
||||
s->write(smVersion);
|
||||
|
||||
// write node names
|
||||
s->write( nodes.size() );
|
||||
for ( S32 i = 0; i < nodes.size(); i++ )
|
||||
writeName( s, nodes[i].nameIndex );
|
||||
|
||||
// legacy write -- write zero objects, don't pretend to support object export anymore
|
||||
s->write( (S32)0 );
|
||||
|
||||
// on import, we will need to adjust keyframe data based on number of
|
||||
// nodes/objects in this shape...number of nodes can be inferred from
|
||||
// above, but number of objects cannot be. Write that quantity here:
|
||||
s->write( objects.size() );
|
||||
|
||||
// write node states -- skip default node states
|
||||
S32 count = seq.rotationMatters.count() * seq.numKeyframes;
|
||||
s->write( count );
|
||||
for ( S32 i = seq.baseRotation; i < seq.baseRotation + count; i++ )
|
||||
{
|
||||
s->write( nodeRotations[i].x );
|
||||
s->write( nodeRotations[i].y );
|
||||
s->write( nodeRotations[i].z );
|
||||
s->write( nodeRotations[i].w );
|
||||
}
|
||||
|
||||
count = seq.translationMatters.count() * seq.numKeyframes;
|
||||
s->write( count );
|
||||
for ( S32 i = seq.baseTranslation; i < seq.baseTranslation + count; i++ )
|
||||
{
|
||||
s->write( nodeTranslations[i].x );
|
||||
s->write( nodeTranslations[i].y );
|
||||
s->write( nodeTranslations[i].z );
|
||||
}
|
||||
|
||||
count = seq.scaleMatters.count() * seq.numKeyframes;
|
||||
if ( seq.animatesUniformScale() )
|
||||
{
|
||||
s->write( count );
|
||||
for ( S32 i = seq.baseScale; i < seq.baseScale + count; i++ )
|
||||
s->write( nodeUniformScales[i] );
|
||||
}
|
||||
else
|
||||
s->write( (S32)0 );
|
||||
|
||||
if ( seq.animatesAlignedScale() )
|
||||
{
|
||||
s->write( count );
|
||||
for ( S32 i = seq.baseScale; i < seq.baseScale + count; i++ )
|
||||
{
|
||||
s->write( nodeAlignedScales[i].x );
|
||||
s->write( nodeAlignedScales[i].y );
|
||||
s->write( nodeAlignedScales[i].z );
|
||||
}
|
||||
}
|
||||
else
|
||||
s->write( (S32)0 );
|
||||
|
||||
if ( seq.animatesArbitraryScale() )
|
||||
{
|
||||
s->write( count );
|
||||
for ( S32 i = seq.baseScale; i < seq.baseScale + count; i++ )
|
||||
{
|
||||
s->write( nodeArbitraryScaleRots[i].x );
|
||||
s->write( nodeArbitraryScaleRots[i].y );
|
||||
s->write( nodeArbitraryScaleRots[i].z );
|
||||
s->write( nodeArbitraryScaleRots[i].w );
|
||||
}
|
||||
for ( S32 i = seq.baseScale; i < seq.baseScale + count; i++ )
|
||||
{
|
||||
s->write( nodeArbitraryScaleFactors[i].x );
|
||||
s->write( nodeArbitraryScaleFactors[i].y );
|
||||
s->write( nodeArbitraryScaleFactors[i].z );
|
||||
}
|
||||
}
|
||||
else
|
||||
s->write( (S32)0 );
|
||||
|
||||
s->write( seq.numGroundFrames );
|
||||
for ( S32 i = seq.firstGroundFrame; i < seq.firstGroundFrame + seq.numGroundFrames; i++ )
|
||||
{
|
||||
s->write( groundTranslations[i].x );
|
||||
s->write( groundTranslations[i].y );
|
||||
s->write( groundTranslations[i].z );
|
||||
}
|
||||
for ( S32 i = seq.firstGroundFrame; i < seq.firstGroundFrame + seq.numGroundFrames; i++ )
|
||||
{
|
||||
s->write( groundRotations[i].x );
|
||||
s->write( groundRotations[i].y );
|
||||
s->write( groundRotations[i].z );
|
||||
s->write( groundRotations[i].w );
|
||||
}
|
||||
|
||||
// write object states -- legacy..no object states
|
||||
s->write( (S32)0 );
|
||||
|
||||
// write the sequence
|
||||
s->write( (S32)1 );
|
||||
writeName( s, seq.nameIndex );
|
||||
{
|
||||
// Write a copy of the sequence with all offsets set to 0
|
||||
TSShape::Sequence tmpSeq(seq);
|
||||
tmpSeq.baseDecalState = 0;
|
||||
tmpSeq.baseObjectState = 0;
|
||||
tmpSeq.baseTranslation = 0;
|
||||
tmpSeq.baseRotation = 0;
|
||||
tmpSeq.baseScale = 0;
|
||||
tmpSeq.firstGroundFrame = 0;
|
||||
tmpSeq.firstTrigger = 0;
|
||||
|
||||
tmpSeq.write( s, false );
|
||||
}
|
||||
|
||||
// write the sequence triggers
|
||||
s->write( seq.numTriggers );
|
||||
for ( S32 i = seq.firstTrigger; i < seq.firstTrigger + seq.numTriggers; i++ )
|
||||
{
|
||||
s->write( triggers[i].state );
|
||||
s->write( triggers[i].pos );
|
||||
}
|
||||
|
||||
smVersion = currentVersion;
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// import sequences into existing shape
|
||||
//-------------------------------------------------
|
||||
bool TSShape::importSequences(Stream * s, const String& sequencePath)
|
||||
{
|
||||
// write version
|
||||
s->read(&smReadVersion);
|
||||
if (smReadVersion>smVersion)
|
||||
{
|
||||
// error -- don't support future version yet :>
|
||||
Con::errorf(ConsoleLogEntry::General,
|
||||
"Sequence import failed: shape exporter newer than running executable.");
|
||||
return false;
|
||||
}
|
||||
if (smReadVersion<19)
|
||||
{
|
||||
// error -- don't support future version yet :>
|
||||
Con::errorf(ConsoleLogEntry::General,
|
||||
"Sequence import failed: deprecated version (%i).",smReadVersion);
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector<S32> nodeMap; // node index of each node from imported sequences
|
||||
Vector<S32> objectMap; // object index of objects from imported sequences
|
||||
VECTOR_SET_ASSOCIATION(nodeMap);
|
||||
VECTOR_SET_ASSOCIATION(objectMap);
|
||||
|
||||
S32 i,sz;
|
||||
|
||||
// read node names
|
||||
// -- this is how we will map imported sequence nodes to our nodes
|
||||
s->read(&sz);
|
||||
nodeMap.setSize(sz);
|
||||
|
||||
for (i=0;i<sz;i++)
|
||||
{
|
||||
U32 startSize = names.size();
|
||||
S32 nameIndex = readName(s,true);
|
||||
|
||||
nodeMap[i] = findNode(nameIndex);
|
||||
|
||||
if (nodeMap[i] < 0)
|
||||
{
|
||||
// node found in sequence but not shape => remove the added node name
|
||||
if (names.size() != startSize)
|
||||
{
|
||||
names.decrement();
|
||||
|
||||
if (names.size() != startSize)
|
||||
Con::errorf(ConsoleLogEntry::General, "TSShape::importSequence: failed to remove unused node correctly for dsq %s.", names[nameIndex].c_str(), sequencePath.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read the following size, but won't do anything with it...legacy: was going to support
|
||||
// import of sequences that animate objects...we don't...
|
||||
s->read(&sz);
|
||||
|
||||
// before reading keyframes, take note of a couple numbers
|
||||
S32 oldShapeNumObjects;
|
||||
s->read(&oldShapeNumObjects);
|
||||
|
||||
// adjust all the new keyframes
|
||||
S32 adjNodeRots = smReadVersion<22 ? nodeRotations.size() - nodeMap.size() : nodeRotations.size();
|
||||
S32 adjNodeTrans = smReadVersion<22 ? nodeTranslations.size() - nodeMap.size() : nodeTranslations.size();
|
||||
S32 adjGroundStates = smReadVersion<22 ? 0 : groundTranslations.size(); // groundTrans==groundRot
|
||||
|
||||
// Read the node states into temporary vectors, then use the
|
||||
// nodeMap to discard unused transforms and map others to our nodes
|
||||
Vector<Quat16> seqRotations;
|
||||
Vector<Point3F> seqTranslations;
|
||||
Vector<F32> seqUniformScales;
|
||||
Vector<Point3F> seqAlignedScales;
|
||||
Vector<Quat16> seqArbitraryScaleRots;
|
||||
Vector<Point3F> seqArbitraryScaleFactors;
|
||||
|
||||
if (smReadVersion>21)
|
||||
{
|
||||
s->read(&sz);
|
||||
seqRotations.setSize(sz);
|
||||
for (i=0; i < sz; i++)
|
||||
{
|
||||
s->read(&seqRotations[i].x);
|
||||
s->read(&seqRotations[i].y);
|
||||
s->read(&seqRotations[i].z);
|
||||
s->read(&seqRotations[i].w);
|
||||
}
|
||||
s->read(&sz);
|
||||
seqTranslations.setSize(sz);
|
||||
for (i=0; i <sz; i++)
|
||||
{
|
||||
s->read(&seqTranslations[i].x);
|
||||
s->read(&seqTranslations[i].y);
|
||||
s->read(&seqTranslations[i].z);
|
||||
}
|
||||
s->read(&sz);
|
||||
seqUniformScales.setSize(sz);
|
||||
for (i = 0; i < sz; i++)
|
||||
s->read(&seqUniformScales[i]);
|
||||
s->read(&sz);
|
||||
seqAlignedScales.setSize(sz);
|
||||
for (i = 0; i < sz; i++)
|
||||
{
|
||||
s->read(&seqAlignedScales[i].x);
|
||||
s->read(&seqAlignedScales[i].y);
|
||||
s->read(&seqAlignedScales[i].z);
|
||||
}
|
||||
s->read(&sz);
|
||||
seqArbitraryScaleRots.setSize(sz);
|
||||
for (i = 0; i <sz; i++)
|
||||
{
|
||||
s->read(&seqArbitraryScaleRots[i].x);
|
||||
s->read(&seqArbitraryScaleRots[i].y);
|
||||
s->read(&seqArbitraryScaleRots[i].z);
|
||||
s->read(&seqArbitraryScaleRots[i].w);
|
||||
}
|
||||
seqArbitraryScaleFactors.setSize(sz);
|
||||
for (i = 0; i < sz; i++)
|
||||
{
|
||||
s->read(&seqArbitraryScaleFactors[i].x);
|
||||
s->read(&seqArbitraryScaleFactors[i].y);
|
||||
s->read(&seqArbitraryScaleFactors[i].z);
|
||||
}
|
||||
|
||||
// ground transforms can be read directly into the shape (none will be
|
||||
// discarded)
|
||||
s->read(&sz);
|
||||
S32 oldSz = groundTranslations.size();
|
||||
groundTranslations.setSize(sz+oldSz);
|
||||
for (i=oldSz;i<sz+oldSz;i++)
|
||||
{
|
||||
s->read(&groundTranslations[i].x);
|
||||
s->read(&groundTranslations[i].y);
|
||||
s->read(&groundTranslations[i].z);
|
||||
}
|
||||
groundRotations.setSize(sz+oldSz);
|
||||
for (i=oldSz;i<sz+oldSz;i++)
|
||||
{
|
||||
s->read(&groundRotations[i].x);
|
||||
s->read(&groundRotations[i].y);
|
||||
s->read(&groundRotations[i].z);
|
||||
s->read(&groundRotations[i].w);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s->read(&sz);
|
||||
seqRotations.setSize(sz);
|
||||
seqTranslations.setSize(sz);
|
||||
for (i = 0; i < sz; i++)
|
||||
{
|
||||
s->read(&seqRotations[i].x);
|
||||
s->read(&seqRotations[i].y);
|
||||
s->read(&seqRotations[i].z);
|
||||
s->read(&seqRotations[i].w);
|
||||
s->read(&seqTranslations[i].x);
|
||||
s->read(&seqTranslations[i].y);
|
||||
s->read(&seqTranslations[i].z);
|
||||
}
|
||||
}
|
||||
|
||||
// add these object states to our own -- shouldn't be any...assume it
|
||||
s->read(&sz);
|
||||
|
||||
// read sequences
|
||||
s->read(&sz);
|
||||
S32 startSeqNum = sequences.size();
|
||||
for (i=0;i<sz;i++)
|
||||
{
|
||||
sequences.increment();
|
||||
Sequence & seq = sequences.last();
|
||||
|
||||
// read name
|
||||
seq.nameIndex = readName(s,true);
|
||||
|
||||
// read the rest of the sequence
|
||||
seq.read(s,false);
|
||||
seq.baseRotation = nodeRotations.size();
|
||||
seq.baseTranslation = nodeTranslations.size();
|
||||
|
||||
if (smReadVersion > 21)
|
||||
{
|
||||
if (seq.animatesUniformScale())
|
||||
seq.baseScale = nodeUniformScales.size();
|
||||
else if (seq.animatesAlignedScale())
|
||||
seq.baseScale = nodeAlignedScales.size();
|
||||
else if (seq.animatesArbitraryScale())
|
||||
seq.baseScale = nodeArbitraryScaleFactors.size();
|
||||
}
|
||||
|
||||
// remap the node matters arrays
|
||||
S32 j;
|
||||
TSIntegerSet newTransMembership;
|
||||
TSIntegerSet newRotMembership;
|
||||
TSIntegerSet newScaleMembership;
|
||||
for (j = 0; j < (S32)nodeMap.size(); j++)
|
||||
{
|
||||
if (nodeMap[j] < 0)
|
||||
continue;
|
||||
|
||||
if (seq.translationMatters.test(j))
|
||||
newTransMembership.set(nodeMap[j]);
|
||||
if (seq.rotationMatters.test(j))
|
||||
newRotMembership.set(nodeMap[j]);
|
||||
if (seq.scaleMatters.test(j))
|
||||
newScaleMembership.set(nodeMap[j]);
|
||||
}
|
||||
|
||||
// resize node transform arrays
|
||||
nodeTranslations.increment(newTransMembership.count() * seq.numKeyframes);
|
||||
nodeRotations.increment(newRotMembership.count() * seq.numKeyframes);
|
||||
if (seq.flags & TSShape::ArbitraryScale)
|
||||
{
|
||||
S32 scaleCount = newScaleMembership.count() * seq.numKeyframes;
|
||||
nodeArbitraryScaleRots.increment(scaleCount);
|
||||
nodeArbitraryScaleFactors.increment(scaleCount);
|
||||
}
|
||||
else if (seq.flags & TSShape::AlignedScale)
|
||||
nodeAlignedScales.increment(newScaleMembership.count() * seq.numKeyframes);
|
||||
else
|
||||
nodeUniformScales.increment(newScaleMembership.count() * seq.numKeyframes);
|
||||
|
||||
// remap node transforms from temporary arrays
|
||||
for (S32 j = 0; j < nodeMap.size(); j++)
|
||||
{
|
||||
if (nodeMap[j] < 0)
|
||||
continue;
|
||||
|
||||
if (newTransMembership.test(nodeMap[j]))
|
||||
{
|
||||
S32 src = seq.numKeyframes * seq.translationMatters.count(j);
|
||||
S32 dest = seq.baseTranslation + seq.numKeyframes * newTransMembership.count(nodeMap[j]);
|
||||
dCopyArray(&nodeTranslations[dest], &seqTranslations[src], seq.numKeyframes);
|
||||
}
|
||||
if (newRotMembership.test(nodeMap[j]))
|
||||
{
|
||||
S32 src = seq.numKeyframes * seq.rotationMatters.count(j);
|
||||
S32 dest = seq.baseRotation + seq.numKeyframes * newRotMembership.count(nodeMap[j]);
|
||||
dCopyArray(&nodeRotations[dest], &seqRotations[src], seq.numKeyframes);
|
||||
}
|
||||
if (newScaleMembership.test(nodeMap[j]))
|
||||
{
|
||||
S32 src = seq.numKeyframes * seq.scaleMatters.count(j);
|
||||
S32 dest = seq.baseScale + seq.numKeyframes * newScaleMembership.count(nodeMap[j]);
|
||||
if (seq.flags & TSShape::ArbitraryScale)
|
||||
{
|
||||
dCopyArray(&nodeArbitraryScaleRots[dest], &seqArbitraryScaleRots[src], seq.numKeyframes);
|
||||
dCopyArray(&nodeArbitraryScaleFactors[dest], &seqArbitraryScaleFactors[src], seq.numKeyframes);
|
||||
}
|
||||
else if (seq.flags & TSShape::AlignedScale)
|
||||
dCopyArray(&nodeAlignedScales[dest], &seqAlignedScales[src], seq.numKeyframes);
|
||||
else
|
||||
dCopyArray(&nodeUniformScales[dest], &seqUniformScales[src], seq.numKeyframes);
|
||||
}
|
||||
}
|
||||
|
||||
seq.translationMatters = newTransMembership;
|
||||
seq.rotationMatters = newRotMembership;
|
||||
seq.scaleMatters = newScaleMembership;
|
||||
|
||||
// adjust trigger numbers...we'll read triggers after sequences...
|
||||
seq.firstTrigger += triggers.size();
|
||||
|
||||
// finally, adjust ground transform's nodes states
|
||||
seq.firstGroundFrame += adjGroundStates;
|
||||
}
|
||||
|
||||
if (smReadVersion<22)
|
||||
{
|
||||
for (i=startSeqNum; 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 j=0;j<seq.numGroundFrames;j++)
|
||||
{
|
||||
groundTranslations[j+oldSz] = nodeTranslations[seq.firstGroundFrame+adjNodeTrans+j];
|
||||
groundRotations[j+oldSz] = nodeRotations[seq.firstGroundFrame+adjNodeRots+j];
|
||||
}
|
||||
seq.firstGroundFrame = oldSz;
|
||||
}
|
||||
}
|
||||
|
||||
// add the new triggers
|
||||
S32 oldSz = triggers.size();
|
||||
s->read(&sz);
|
||||
triggers.setSize(oldSz+sz);
|
||||
for (S32 i=0; i<sz;i++)
|
||||
{
|
||||
s->read(&triggers[i+oldSz].state);
|
||||
s->read(&triggers[i+oldSz].pos);
|
||||
}
|
||||
|
||||
if (smInitOnRead)
|
||||
init();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// read/write sequence
|
||||
//-------------------------------------------------
|
||||
void TSShape::Sequence::read(Stream * s, bool readNameIndex)
|
||||
{
|
||||
AssertISV(smReadVersion>=19,"Reading old sequence");
|
||||
|
||||
if (readNameIndex)
|
||||
s->read(&nameIndex);
|
||||
flags = 0;
|
||||
if (TSShape::smReadVersion>21)
|
||||
s->read(&flags);
|
||||
else
|
||||
flags=0;
|
||||
|
||||
s->read(&numKeyframes);
|
||||
s->read(&duration);
|
||||
|
||||
if (TSShape::smReadVersion<22)
|
||||
{
|
||||
bool tmp = false;
|
||||
s->read(&tmp);
|
||||
if (tmp)
|
||||
flags |= Blend;
|
||||
s->read(&tmp);
|
||||
if (tmp)
|
||||
flags |= Cyclic;
|
||||
s->read(&tmp);
|
||||
if (tmp)
|
||||
flags |= MakePath;
|
||||
}
|
||||
|
||||
s->read(&priority);
|
||||
s->read(&firstGroundFrame);
|
||||
s->read(&numGroundFrames);
|
||||
if (TSShape::smReadVersion>21)
|
||||
{
|
||||
s->read(&baseRotation);
|
||||
s->read(&baseTranslation);
|
||||
s->read(&baseScale);
|
||||
s->read(&baseObjectState);
|
||||
s->read(&baseDecalState);
|
||||
}
|
||||
else
|
||||
{
|
||||
s->read(&baseRotation);
|
||||
baseTranslation=baseRotation;
|
||||
s->read(&baseObjectState);
|
||||
s->read(&baseDecalState);
|
||||
}
|
||||
|
||||
s->read(&firstTrigger);
|
||||
s->read(&numTriggers);
|
||||
s->read(&toolBegin);
|
||||
|
||||
// now the membership sets:
|
||||
rotationMatters.read(s);
|
||||
if (TSShape::smReadVersion<22)
|
||||
translationMatters=rotationMatters;
|
||||
else
|
||||
{
|
||||
translationMatters.read(s);
|
||||
scaleMatters.read(s);
|
||||
}
|
||||
|
||||
TSIntegerSet dummy;
|
||||
dummy.read(s); // DEPRECIATED: Decals
|
||||
dummy.read(s); // DEPRECIATED: Ifl materials
|
||||
|
||||
visMatters.read(s);
|
||||
frameMatters.read(s);
|
||||
matFrameMatters.read(s);
|
||||
|
||||
dirtyFlags = 0;
|
||||
if (rotationMatters.testAll() || translationMatters.testAll() || scaleMatters.testAll())
|
||||
dirtyFlags |= TSShapeInstance::TransformDirty;
|
||||
if (visMatters.testAll())
|
||||
dirtyFlags |= TSShapeInstance::VisDirty;
|
||||
if (frameMatters.testAll())
|
||||
dirtyFlags |= TSShapeInstance::FrameDirty;
|
||||
if (matFrameMatters.testAll())
|
||||
dirtyFlags |= TSShapeInstance::MatFrameDirty;
|
||||
}
|
||||
|
||||
void TSShape::Sequence::write(Stream * s, bool writeNameIndex) const
|
||||
{
|
||||
if (writeNameIndex)
|
||||
s->write(nameIndex);
|
||||
s->write(flags);
|
||||
s->write(numKeyframes);
|
||||
s->write(duration);
|
||||
s->write(priority);
|
||||
s->write(firstGroundFrame);
|
||||
s->write(numGroundFrames);
|
||||
s->write(baseRotation);
|
||||
s->write(baseTranslation);
|
||||
s->write(baseScale);
|
||||
s->write(baseObjectState);
|
||||
s->write(baseDecalState);
|
||||
s->write(firstTrigger);
|
||||
s->write(numTriggers);
|
||||
s->write(toolBegin);
|
||||
|
||||
// now the membership sets:
|
||||
rotationMatters.write(s);
|
||||
translationMatters.write(s);
|
||||
scaleMatters.write(s);
|
||||
|
||||
TSIntegerSet dummy;
|
||||
dummy.write(s); // DEPRECIATED: Decals
|
||||
dummy.write(s); // DEPRECIATED: Ifl materials
|
||||
|
||||
visMatters.write(s);
|
||||
frameMatters.write(s);
|
||||
matFrameMatters.write(s);
|
||||
}
|
||||
|
||||
void TSShape::writeName(Stream * s, S32 nameIndex)
|
||||
{
|
||||
const char * name = "";
|
||||
if (nameIndex>=0)
|
||||
name = names[nameIndex];
|
||||
S32 sz = (S32)dStrlen(name);
|
||||
s->write(sz);
|
||||
if (sz)
|
||||
s->write(sz*sizeof(char),name);
|
||||
}
|
||||
|
||||
S32 TSShape::readName(Stream * s, bool addName)
|
||||
{
|
||||
static char buffer[256];
|
||||
S32 sz;
|
||||
S32 nameIndex = -1;
|
||||
s->read(&sz);
|
||||
if (sz)
|
||||
{
|
||||
s->read(sz*sizeof(char),buffer);
|
||||
buffer[sz] = '\0';
|
||||
nameIndex = findName(buffer);
|
||||
|
||||
// Many modeling apps don't support spaces in names, so if the lookup
|
||||
// failed, try the name again with spaces replaced by underscores
|
||||
if (nameIndex < 0)
|
||||
{
|
||||
while (char *p = dStrchr(buffer, ' '))
|
||||
*p = '_';
|
||||
nameIndex = findName(buffer);
|
||||
}
|
||||
|
||||
if (nameIndex<0 && addName)
|
||||
{
|
||||
nameIndex = names.size();
|
||||
names.increment();
|
||||
names.last() = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
return nameIndex;
|
||||
}
|
||||
168
Engine/source/ts/tsSortedMesh.cpp
Normal file
168
Engine/source/ts/tsSortedMesh.cpp
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "ts/tsSortedMesh.h"
|
||||
#include "math/mMath.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
|
||||
// Not worth the effort, much less the effort to comment, but if the draw types
|
||||
// are consecutive use addition rather than a table to go from index to command value...
|
||||
/*
|
||||
#if ((GL_TRIANGLES+1==GL_TRIANGLE_STRIP) && (GL_TRIANGLE_STRIP+1==GL_TRIANGLE_FAN))
|
||||
#define getDrawType(a) (GL_TRIANGLES+(a))
|
||||
#else
|
||||
U32 drawTypes[] = { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN };
|
||||
#define getDrawType(a) (drawTypes[a])
|
||||
#endif
|
||||
*/
|
||||
|
||||
// found in tsmesh
|
||||
extern void forceFaceCamera();
|
||||
extern void forceFaceCameraZAxis();
|
||||
|
||||
//-----------------------------------------------------
|
||||
// TSSortedMesh render methods
|
||||
//-----------------------------------------------------
|
||||
|
||||
void TSSortedMesh::render(S32 frame, S32 matFrame, TSMaterialList * materials)
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
// TSSortedMesh collision methods
|
||||
//-----------------------------------------------------
|
||||
|
||||
bool TSSortedMesh::buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials )
|
||||
{
|
||||
TORQUE_UNUSED(frame);
|
||||
TORQUE_UNUSED(polyList);
|
||||
TORQUE_UNUSED(surfaceKey);
|
||||
TORQUE_UNUSED(materials);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TSSortedMesh::castRay( S32 frame, const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials )
|
||||
{
|
||||
TORQUE_UNUSED(frame);
|
||||
TORQUE_UNUSED(start);
|
||||
TORQUE_UNUSED(end);
|
||||
TORQUE_UNUSED(rayInfo);
|
||||
TORQUE_UNUSED(materials);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TSSortedMesh::buildConvexHull()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
S32 TSSortedMesh::getNumPolys()
|
||||
{
|
||||
S32 count = 0;
|
||||
S32 cIdx = !clusters.size() ? -1 : 0;
|
||||
while (cIdx>=0)
|
||||
{
|
||||
Cluster & cluster = clusters[cIdx];
|
||||
for (S32 i=cluster.startPrimitive; i<cluster.endPrimitive; i++)
|
||||
{
|
||||
if (primitives[i].matIndex & TSDrawPrimitive::Triangles)
|
||||
count += primitives[i].numElements / 3;
|
||||
else
|
||||
count += primitives[i].numElements - 2;
|
||||
}
|
||||
cIdx = cluster.frontCluster; // always use frontCluster...we assume about the same no matter what
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
// TSSortedMesh assembly/dissembly methods
|
||||
// used for transfer to/from memory buffers
|
||||
//-----------------------------------------------------
|
||||
|
||||
#define tsalloc TSShape::smTSAlloc
|
||||
|
||||
void TSSortedMesh::assemble(bool skip)
|
||||
{
|
||||
bool save1 = TSMesh::smUseTriangles;
|
||||
bool save2 = TSMesh::smUseOneStrip;
|
||||
TSMesh::smUseTriangles = false;
|
||||
TSMesh::smUseOneStrip = false;
|
||||
|
||||
TSMesh::assemble(skip);
|
||||
|
||||
TSMesh::smUseTriangles = save1;
|
||||
TSMesh::smUseOneStrip = save2;
|
||||
|
||||
S32 numClusters = tsalloc.get32();
|
||||
S32 * ptr32 = tsalloc.copyToShape32(numClusters*8);
|
||||
clusters.set(ptr32,numClusters);
|
||||
|
||||
S32 sz = tsalloc.get32();
|
||||
ptr32 = tsalloc.copyToShape32(sz);
|
||||
startCluster.set(ptr32,sz);
|
||||
|
||||
sz = tsalloc.get32();
|
||||
ptr32 = tsalloc.copyToShape32(sz);
|
||||
firstVerts.set(ptr32,sz);
|
||||
|
||||
sz = tsalloc.get32();
|
||||
ptr32 = tsalloc.copyToShape32(sz);
|
||||
numVerts.set(ptr32,sz);
|
||||
|
||||
sz = tsalloc.get32();
|
||||
ptr32 = tsalloc.copyToShape32(sz);
|
||||
firstTVerts.set(ptr32,sz);
|
||||
|
||||
alwaysWriteDepth = tsalloc.get32()!=0;
|
||||
|
||||
tsalloc.checkGuard();
|
||||
}
|
||||
|
||||
void TSSortedMesh::disassemble()
|
||||
{
|
||||
TSMesh::disassemble();
|
||||
|
||||
tsalloc.set32(clusters.size());
|
||||
tsalloc.copyToBuffer32((S32*)clusters.address(),clusters.size()*8);
|
||||
|
||||
tsalloc.set32(startCluster.size());
|
||||
tsalloc.copyToBuffer32((S32*)startCluster.address(),startCluster.size());
|
||||
|
||||
tsalloc.set32(firstVerts.size());
|
||||
tsalloc.copyToBuffer32((S32*)firstVerts.address(),firstVerts.size());
|
||||
|
||||
tsalloc.set32(numVerts.size());
|
||||
tsalloc.copyToBuffer32((S32*)numVerts.address(),numVerts.size());
|
||||
|
||||
tsalloc.set32(firstTVerts.size());
|
||||
tsalloc.copyToBuffer32((S32*)firstTVerts.address(),firstTVerts.size());
|
||||
|
||||
tsalloc.set32(alwaysWriteDepth ? 1 : 0);
|
||||
|
||||
tsalloc.setGuard();
|
||||
}
|
||||
|
||||
|
||||
|
||||
80
Engine/source/ts/tsSortedMesh.h
Normal file
80
Engine/source/ts/tsSortedMesh.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSSORTEDMESH_H_
|
||||
#define _TSSORTEDMESH_H_
|
||||
|
||||
#ifndef _TSMESH_H_
|
||||
#include "ts/tsMesh.h"
|
||||
#endif
|
||||
|
||||
/// TSSortedMesh is for meshes that need sorting (obviously). Such meshes
|
||||
/// are usually partially or completely composed of translucent/parent polygons.
|
||||
class TSSortedMesh : public TSMesh
|
||||
{
|
||||
public:
|
||||
typedef TSMesh Parent;
|
||||
|
||||
/// This is a group of primitives that belong "together" in the rendering sequence.
|
||||
/// For example, if a player model had a helmet with a translucent visor, the visor
|
||||
/// would be a Cluster.
|
||||
struct Cluster
|
||||
{
|
||||
S32 startPrimitive;
|
||||
S32 endPrimitive;
|
||||
Point3F normal;
|
||||
F32 k;
|
||||
S32 frontCluster; ///< go to this cluster if in front of plane, if frontCluster<0, no cluster
|
||||
S32 backCluster; ///< go to this cluster if in back of plane, if backCluster<0, no cluster
|
||||
///< if frontCluster==backCluster, no plane to test against...
|
||||
};
|
||||
|
||||
Vector<Cluster> clusters; ///< All of the clusters of primitives to be drawn
|
||||
Vector<S32> startCluster; ///< indexed by frame number
|
||||
Vector<S32> firstVerts; ///< indexed by frame number
|
||||
Vector<S32> numVerts; ///< indexed by frame number
|
||||
Vector<S32> firstTVerts; ///< indexed by frame number or matFrame number, depending on which one animates (never both)
|
||||
|
||||
/// sometimes, we want to write the depth value to the frame buffer even when object is translucent
|
||||
bool alwaysWriteDepth;
|
||||
|
||||
// render methods..
|
||||
void render(S32 frame, S32 matFrame, TSMaterialList *);
|
||||
|
||||
bool buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials );
|
||||
bool castRay( S32 frame, const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials );
|
||||
bool buildConvexHull(); ///< does nothing, skins don't use this
|
||||
///
|
||||
/// @returns false ALWAYS
|
||||
S32 getNumPolys();
|
||||
|
||||
void assemble(bool skip);
|
||||
void disassemble();
|
||||
|
||||
TSSortedMesh() {
|
||||
meshType = SortedMeshType;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _TS_SORTED_MESH
|
||||
|
||||
|
||||
835
Engine/source/ts/tsThread.cpp
Normal file
835
Engine/source/ts/tsThread.cpp
Normal file
|
|
@ -0,0 +1,835 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/tsShapeInstance.h"
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// This file contains the shape instance thread class (defined in tsShapeInstance.h)
|
||||
// and the tsShapeInstance functions to interface with the thread class.
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Thread class
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
// given a position on the thread, choose correct keyframes
|
||||
// slight difference between one-shot and cyclic sequences -- see comments below for details
|
||||
void TSThread::selectKeyframes(F32 pos, const TSSequence * seq, S32 * k1, S32 * k2, F32 * kpos)
|
||||
{
|
||||
S32 numKF = seq->numKeyframes;
|
||||
F32 kf;
|
||||
|
||||
if (seq->isCyclic())
|
||||
{
|
||||
// cyclic sequence:
|
||||
// pos=0 and pos=1 are equivalent, so we don't have a keyframe at pos=1
|
||||
// last keyframe corresponds to pos=n/(n-1) up to (not including) pos=1
|
||||
// (where n == num keyframes)
|
||||
|
||||
AssertFatal(pos>=0.0f && pos<1.0f,"TSThread::selectKeyframes");
|
||||
|
||||
kf = pos * (F32) (numKF);
|
||||
|
||||
// set keyPos
|
||||
if (kpos)
|
||||
*kpos = kf - (S32) kf;
|
||||
|
||||
// make sure compiler doing what we want...
|
||||
AssertFatal(*kpos>=0.0f && *kpos<1.0f,"TSThread::selectKeyframes");
|
||||
|
||||
S32 kfIdx1 = (S32) kf;
|
||||
|
||||
// following assert could happen if pos1<1 && pos1==1...paradoxically...
|
||||
AssertFatal(kfIdx1<=seq->numKeyframes,"TSThread::selectKeyframes");
|
||||
|
||||
S32 kfIdx2 = (kfIdx1==seq->numKeyframes-1) ? 0 : kfIdx1+1;
|
||||
|
||||
if (k1)
|
||||
*k1 = kfIdx1;
|
||||
if (k2)
|
||||
*k2 = kfIdx2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// one-shot sequence:
|
||||
// pos=0 and pos=1 are now different, so we have a keyframe at pos=1
|
||||
// last keyframe corresponds to pos=1
|
||||
// rest of the keyframes are equally spaced (so 1/(n-1) pos units long)
|
||||
// (where n == num keyframes)
|
||||
|
||||
AssertFatal(pos>=0.0f && pos<=1.0f,"TSThread::selectKeyframes");
|
||||
|
||||
if (pos==1.0f)
|
||||
{
|
||||
if (kpos)
|
||||
*kpos = 0.0f;
|
||||
if (k1)
|
||||
*k1 = seq->numKeyframes-1;
|
||||
if (k2)
|
||||
*k2 = seq->numKeyframes-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
kf = pos * (F32) (numKF-1);
|
||||
|
||||
// set keyPos
|
||||
if (kpos)
|
||||
*kpos = kf - (S32) kf;
|
||||
|
||||
S32 kfIdx1 = (S32) kf;
|
||||
|
||||
// following assert could happen if pos1<1 && pos1==1...paradoxically...
|
||||
AssertFatal(kfIdx1<seq->numKeyframes,"TSThread::selectKeyFrames: invalid keyframe!");
|
||||
|
||||
S32 kfIdx2 = kfIdx1+1;
|
||||
|
||||
if (k1)
|
||||
*k1 = kfIdx1;
|
||||
if (k2)
|
||||
*k2 = kfIdx2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TSThread::getGround(F32 t, MatrixF * pMat)
|
||||
{
|
||||
static const QuatF unitRotation(0,0,0,1);
|
||||
static const Point3F unitTranslation = Point3F::Zero;
|
||||
|
||||
const QuatF * q1, * q2;
|
||||
QuatF rot1,rot2;
|
||||
const Point3F * p1, * p2;
|
||||
|
||||
// if N = sequence->numGroundFrames, then there are N+1 positions we
|
||||
// interpolate betweeen: 0/N, 1/N ... N/N
|
||||
// we need to convert the p passed to us into 2 ground keyframes:
|
||||
|
||||
// the 0.99999f is in case 'p' is exactly 1.0f, which is legal but 'kf'
|
||||
// needs to be strictly less than 'sequence->numGroundFrames'
|
||||
F32 kf = 0.999999f * t * (F32) getSequence()->numGroundFrames;
|
||||
|
||||
// get frame number and interp param (kpos)
|
||||
S32 frame = (S32)kf;
|
||||
F32 kpos = kf - (F32)frame;
|
||||
|
||||
// now point pT1 and pT2 at transforms for keyframes 'frame' and 'frame+1'
|
||||
|
||||
// following a little strange: first ground keyframe (0/N in comment above) is
|
||||
// assumed to be ident. and not found in the list.
|
||||
if (frame)
|
||||
{
|
||||
p1 = &mShapeInstance->mShape->groundTranslations[getSequence()->firstGroundFrame + frame - 1];
|
||||
q1 = &mShapeInstance->mShape->groundRotations[getSequence()->firstGroundFrame + frame - 1].getQuatF(&rot1);
|
||||
}
|
||||
else
|
||||
{
|
||||
p1 = &unitTranslation;
|
||||
q1 = &unitRotation;
|
||||
}
|
||||
|
||||
// similar to above, ground keyframe number 'frame+1' is actually offset by 'frame'
|
||||
p2 = &mShapeInstance->mShape->groundTranslations[getSequence()->firstGroundFrame + frame];
|
||||
q2 = &mShapeInstance->mShape->groundRotations[getSequence()->firstGroundFrame + frame].getQuatF(&rot2);
|
||||
|
||||
QuatF q;
|
||||
Point3F p;
|
||||
TSTransform::interpolate(*q1,*q2,kpos,&q);
|
||||
TSTransform::interpolate(*p1,*p2,kpos,&p);
|
||||
TSTransform::setMatrix(q,p,pMat);
|
||||
}
|
||||
|
||||
void TSThread::setSequence(S32 seq, F32 toPos)
|
||||
{
|
||||
const TSShape * shape = mShapeInstance->mShape;
|
||||
|
||||
AssertFatal(shape && shape->sequences.size()>seq && toPos>=0.0f && toPos<=1.0f,
|
||||
"TSThread::setSequence: invalid shape handle, sequence number, or position.");
|
||||
|
||||
mShapeInstance->clearTransition(this);
|
||||
|
||||
sequence = seq;
|
||||
priority = getSequence()->priority;
|
||||
pos = toPos;
|
||||
makePath = getSequence()->makePath();
|
||||
path.start = path.end = 0;
|
||||
path.loop = 0;
|
||||
|
||||
// 1.0f doesn't exist on cyclic sequences
|
||||
if (pos>0.9999f && getSequence()->isCyclic())
|
||||
pos = 0.9999f;
|
||||
|
||||
// select keyframes
|
||||
selectKeyframes(pos,getSequence(),&keyNum1,&keyNum2,&keyPos);
|
||||
}
|
||||
|
||||
void TSThread::transitionToSequence(S32 seq, F32 toPos, F32 duration, bool continuePlay)
|
||||
{
|
||||
AssertFatal(duration>=0.0f,"TSThread::transitionToSequence: negative duration not allowed");
|
||||
|
||||
// make sure these nodes are smoothly interpolated to new positions...
|
||||
// basically, any node we controlled just prior to transition, or at any stage
|
||||
// of the transition is interpolated. If we start to transtion from A to B,
|
||||
// but before reaching B we transtion to C, we interpolate all nodes controlled
|
||||
// by A, B, or C to their new position.
|
||||
if (transitionData.inTransition)
|
||||
{
|
||||
transitionData.oldRotationNodes.overlap(getSequence()->rotationMatters);
|
||||
transitionData.oldTranslationNodes.overlap(getSequence()->translationMatters);
|
||||
transitionData.oldScaleNodes.overlap(getSequence()->scaleMatters);
|
||||
}
|
||||
else
|
||||
{
|
||||
transitionData.oldRotationNodes = getSequence()->rotationMatters;
|
||||
transitionData.oldTranslationNodes = getSequence()->translationMatters;
|
||||
transitionData.oldScaleNodes = getSequence()->scaleMatters;
|
||||
}
|
||||
|
||||
// set time characteristics of transition
|
||||
transitionData.oldSequence = sequence;
|
||||
transitionData.oldPos = pos;
|
||||
transitionData.duration = duration;
|
||||
transitionData.pos = 0.0f;
|
||||
transitionData.direction = timeScale>0.0f ? 1.0f : -1.0f;
|
||||
transitionData.targetScale = continuePlay ? 1.0f : 0.0f;
|
||||
|
||||
// in transition...
|
||||
transitionData.inTransition = true;
|
||||
|
||||
// set target sequence data
|
||||
sequence = seq;
|
||||
priority = getSequence()->priority;
|
||||
pos = toPos;
|
||||
makePath = getSequence()->makePath();
|
||||
path.start = path.end = 0;
|
||||
path.loop = 0;
|
||||
|
||||
// 1.0f doesn't exist on cyclic sequences
|
||||
if (pos>0.9999f && getSequence()->isCyclic())
|
||||
pos = 0.9999f;
|
||||
|
||||
// select keyframes
|
||||
selectKeyframes(pos,getSequence(),&keyNum1,&keyNum2,&keyPos);
|
||||
}
|
||||
|
||||
bool TSThread::isInTransition()
|
||||
{
|
||||
return transitionData.inTransition;
|
||||
}
|
||||
|
||||
void TSThread::animateTriggers()
|
||||
{
|
||||
if (!getSequence()->numTriggers)
|
||||
return;
|
||||
|
||||
switch (path.loop)
|
||||
{
|
||||
case -1 :
|
||||
activateTriggers(path.start,0);
|
||||
activateTriggers(1,path.end);
|
||||
break;
|
||||
case 0 :
|
||||
activateTriggers(path.start,path.end);
|
||||
break;
|
||||
case 1 :
|
||||
activateTriggers(path.start,1);
|
||||
activateTriggers(0,path.end);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if (path.loop>0)
|
||||
{
|
||||
activateTriggers(path.end,1);
|
||||
activateTriggers(0,path.end);
|
||||
}
|
||||
else
|
||||
{
|
||||
activateTriggers(path.end,0);
|
||||
activateTriggers(1,path.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TSThread::activateTriggers(F32 a, F32 b)
|
||||
{
|
||||
S32 i;
|
||||
const TSShape * shape = mShapeInstance->mShape;
|
||||
S32 firstTrigger = getSequence()->firstTrigger;
|
||||
S32 numTriggers = getSequence()->numTriggers;
|
||||
|
||||
// first find triggers at position a and b
|
||||
// we assume there aren't many triggers, so
|
||||
// search is linear
|
||||
F32 lastPos = -1.0f;
|
||||
S32 aIndex = numTriggers+firstTrigger; // initialized to handle case where pos past all triggers
|
||||
S32 bIndex = numTriggers+firstTrigger; // initialized to handle case where pos past all triggers
|
||||
for (i=firstTrigger; i<numTriggers+firstTrigger; i++)
|
||||
{
|
||||
// is a between this trigger and previous one...
|
||||
if (a>lastPos && a<=shape->triggers[i].pos)
|
||||
aIndex = i;
|
||||
// is b between this trigger and previous one...
|
||||
if (b>lastPos && b<=shape->triggers[i].pos)
|
||||
bIndex = i;
|
||||
lastPos = shape->triggers[i].pos;
|
||||
}
|
||||
|
||||
// activate triggers between aIndex and bIndex (depends on direction)
|
||||
if (aIndex<=bIndex)
|
||||
{
|
||||
for (i=aIndex; i<bIndex; i++)
|
||||
{
|
||||
U32 state = shape->triggers[i].state;
|
||||
bool on = (state & TSShape::Trigger::StateOn)!=0;
|
||||
mShapeInstance->setTriggerStateBit(state & TSShape::Trigger::StateMask, on);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=aIndex-1; i>=bIndex; i--)
|
||||
{
|
||||
U32 state = shape->triggers[i].state;
|
||||
bool on = (state & TSShape::Trigger::StateOn)!=0;
|
||||
if (state & TSShape::Trigger::InvertOnReverse)
|
||||
on = !on;
|
||||
mShapeInstance->setTriggerStateBit(state & TSShape::Trigger::StateMask, on);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
F32 TSThread::getPos()
|
||||
{
|
||||
return transitionData.inTransition ? transitionData.pos : pos;
|
||||
}
|
||||
|
||||
F32 TSThread::getTime()
|
||||
{
|
||||
return transitionData.inTransition ? transitionData.pos * transitionData.duration : pos * getSequence()->duration;
|
||||
}
|
||||
|
||||
F32 TSThread::getDuration()
|
||||
{
|
||||
return transitionData.inTransition ? transitionData.duration : getSequence()->duration;
|
||||
}
|
||||
|
||||
F32 TSThread::getScaledDuration()
|
||||
{
|
||||
return getDuration() / mFabs(timeScale);
|
||||
}
|
||||
|
||||
F32 TSThread::getTimeScale()
|
||||
{
|
||||
return timeScale;
|
||||
}
|
||||
|
||||
void TSThread::setTimeScale(F32 ts)
|
||||
{
|
||||
timeScale = ts;
|
||||
}
|
||||
|
||||
void TSThread::advancePos(F32 delta)
|
||||
{
|
||||
if (mFabs(delta)>0.00001f)
|
||||
{
|
||||
// make dirty what this thread changes
|
||||
U32 dirtyFlags = getSequence()->dirtyFlags | (transitionData.inTransition ? TSShapeInstance::TransformDirty : 0);
|
||||
for (S32 i=0; i<mShapeInstance->getShape()->subShapeFirstNode.size(); i++)
|
||||
mShapeInstance->mDirtyFlags[i] |= dirtyFlags;
|
||||
}
|
||||
|
||||
if (transitionData.inTransition)
|
||||
{
|
||||
transitionData.pos += transitionData.direction * delta;
|
||||
if (transitionData.pos<0 || transitionData.pos>=1.0f)
|
||||
{
|
||||
mShapeInstance->clearTransition(this);
|
||||
if (transitionData.pos<0.0f)
|
||||
// return to old sequence
|
||||
mShapeInstance->setSequence(this,transitionData.oldSequence,transitionData.oldPos);
|
||||
}
|
||||
// re-adjust delta to be correct time-wise
|
||||
delta *= transitionData.targetScale * transitionData.duration / getSequence()->duration;
|
||||
}
|
||||
|
||||
// even if we are in a transition, keep playing the sequence
|
||||
|
||||
if (makePath)
|
||||
{
|
||||
path.start = pos;
|
||||
pos += delta;
|
||||
if (!getSequence()->isCyclic())
|
||||
{
|
||||
pos = mClampF(pos , 0.0f, 1.0f);
|
||||
path.loop = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
path.loop = (S32)pos;
|
||||
if (pos < 0.0f)
|
||||
path.loop--;
|
||||
pos -= path.loop;
|
||||
// following necessary because of floating point roundoff errors
|
||||
if (pos < 0.0f) pos += 1.0f;
|
||||
if (pos >= 1.0f) pos -= 1.0f;
|
||||
}
|
||||
path.end = pos;
|
||||
|
||||
animateTriggers(); // do this automatically...no need for user to call it
|
||||
|
||||
AssertFatal(pos>=0.0f && pos<=1.0f,"TSThread::advancePos (1)");
|
||||
AssertFatal(!getSequence()->isCyclic() || pos<1.0f,"TSThread::advancePos (2)");
|
||||
}
|
||||
else
|
||||
{
|
||||
pos += delta;
|
||||
if (!getSequence()->isCyclic())
|
||||
pos = mClampF(pos, 0.0f, 1.0f);
|
||||
else
|
||||
{
|
||||
pos -= S32(pos);
|
||||
// following necessary because of floating point roundoff errors
|
||||
if (pos < 0.0f) pos += 1.0f;
|
||||
if (pos >= 1.0f) pos -= 1.0f;
|
||||
}
|
||||
AssertFatal(pos>=0.0f && pos<=1.0f,"TSThread::advancePos (3)");
|
||||
AssertFatal(!getSequence()->isCyclic() || pos<1.0f,"TSThread::advancePos (4)");
|
||||
}
|
||||
|
||||
// select keyframes
|
||||
selectKeyframes(pos,getSequence(),&keyNum1,&keyNum2,&keyPos);
|
||||
}
|
||||
|
||||
void TSThread::advanceTime(F32 delta)
|
||||
{
|
||||
advancePos(timeScale * delta / getDuration());
|
||||
}
|
||||
|
||||
void TSThread::setPos(F32 pos)
|
||||
{
|
||||
advancePos(pos-getPos());
|
||||
}
|
||||
|
||||
void TSThread::setTime(F32 time)
|
||||
{
|
||||
setPos(timeScale * time/getDuration());
|
||||
}
|
||||
|
||||
S32 TSThread::getKeyframeCount()
|
||||
{
|
||||
AssertFatal(!transitionData.inTransition,"TSThread::getKeyframeCount: not while in transition");
|
||||
|
||||
return getSequence()->numKeyframes + 1;
|
||||
}
|
||||
|
||||
S32 TSThread::getKeyframeNumber()
|
||||
{
|
||||
AssertFatal(!transitionData.inTransition,"TSThread::getKeyframeNumber: not while in transition");
|
||||
|
||||
return keyNum1;
|
||||
}
|
||||
|
||||
void TSThread::setKeyframeNumber(S32 kf)
|
||||
{
|
||||
AssertFatal(kf>=0 && kf<= getSequence()->numKeyframes,
|
||||
"TSThread::setKeyframeNumber: invalid frame specified.");
|
||||
AssertFatal(!transitionData.inTransition,"TSThread::setKeyframeNumber: not while in transition");
|
||||
|
||||
keyNum1 = keyNum2 = kf;
|
||||
keyPos = 0;
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
TSThread::TSThread(TSShapeInstance * _shapeInst)
|
||||
{
|
||||
timeScale = 1.0f;
|
||||
mShapeInstance = _shapeInst;
|
||||
transitionData.inTransition = false;
|
||||
blendDisabled = false;
|
||||
setSequence(0,0.0f);
|
||||
}
|
||||
|
||||
S32 TSThread::operator<(const TSThread & th2) const
|
||||
{
|
||||
if (getSequence()->isBlend() == th2.getSequence()->isBlend())
|
||||
{
|
||||
// both blend or neither blend, sort based on priority only -- higher priority first
|
||||
S32 ret = 0; // do it this way to (hopefully) take advantage of 'conditional move' assembly instruction
|
||||
if (priority > th2.priority)
|
||||
ret = -1;
|
||||
if (th2.priority > priority)
|
||||
ret = 1;
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
// one is blend, the other is not...sort based on blend -- non-blended first
|
||||
AssertFatal(!getSequence()->isBlend() || !th2.getSequence()->isBlend(),"compareThreads: unequal 'trues'");
|
||||
|
||||
S32 ret = -1; // do it this way to (hopefully) take advantage of 'conditional move' assembly instruction
|
||||
if (getSequence()->isBlend())
|
||||
ret = 1;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// TSShapeInstance Thread Interface -- more implemented in header file
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
TSThread * TSShapeInstance::addThread()
|
||||
{
|
||||
if (mShape->sequences.empty())
|
||||
return NULL;
|
||||
|
||||
mThreadList.increment();
|
||||
mThreadList.last() = new TSThread(this);
|
||||
setDirty(AllDirtyMask);
|
||||
return mThreadList.last();
|
||||
}
|
||||
|
||||
TSThread * TSShapeInstance::getThread(S32 threadNumber)
|
||||
{
|
||||
AssertFatal(threadNumber < mThreadList.size() && threadNumber>=0,"TSShapeInstance::getThread: threadNumber out of bounds.");
|
||||
return mThreadList[threadNumber];
|
||||
}
|
||||
|
||||
void TSShapeInstance::destroyThread(TSThread * thread)
|
||||
{
|
||||
if (!thread)
|
||||
return;
|
||||
|
||||
clearTransition(thread);
|
||||
|
||||
S32 i;
|
||||
for (i=0; i<mThreadList.size(); i++)
|
||||
if (thread==mThreadList[i])
|
||||
break;
|
||||
|
||||
AssertFatal(i<mThreadList.size(),"TSShapeInstance::destroyThread was requested to destroy a thread that this instance doesn't own!");
|
||||
|
||||
delete mThreadList[i];
|
||||
mThreadList.erase(i);
|
||||
setDirty(AllDirtyMask);
|
||||
checkScaleCurrentlyAnimated();
|
||||
}
|
||||
|
||||
U32 TSShapeInstance::threadCount()
|
||||
{
|
||||
return mThreadList.size();
|
||||
}
|
||||
|
||||
void TSShapeInstance::setSequence(TSThread * thread, S32 seq, F32 pos)
|
||||
{
|
||||
if ( (thread->transitionData.inTransition && mTransitionThreads.size()>1) || mTransitionThreads.size()>0)
|
||||
{
|
||||
// if we have transitions, make sure transforms are up to date...
|
||||
sortThreads();
|
||||
animateNodeSubtrees();
|
||||
}
|
||||
|
||||
thread->setSequence(seq,pos);
|
||||
setDirty(AllDirtyMask);
|
||||
mGroundThread = NULL;
|
||||
|
||||
if (mScaleCurrentlyAnimated && !thread->getSequence()->animatesScale())
|
||||
checkScaleCurrentlyAnimated();
|
||||
else if (!mScaleCurrentlyAnimated && thread->getSequence()->animatesScale())
|
||||
mScaleCurrentlyAnimated=true;
|
||||
|
||||
updateTransitions();
|
||||
}
|
||||
|
||||
U32 TSShapeInstance::getSequence(TSThread * thread)
|
||||
{
|
||||
//AssertFatal( thread->sequence >= 0, "TSShapeInstance::getSequence: range error A");
|
||||
//AssertFatal( thread->sequence < mShape->sequences.size(), "TSShapeInstance::getSequence: range error B");
|
||||
return (U32)thread->sequence;
|
||||
}
|
||||
|
||||
void TSShapeInstance::transitionToSequence(TSThread * thread, S32 seq, F32 pos, F32 duration, bool continuePlay)
|
||||
{
|
||||
// make sure all transforms on all detail levels are accurate
|
||||
sortThreads();
|
||||
animateNodeSubtrees();
|
||||
|
||||
thread->transitionToSequence(seq,pos,duration,continuePlay);
|
||||
setDirty(AllDirtyMask);
|
||||
mGroundThread = NULL;
|
||||
|
||||
if (mScaleCurrentlyAnimated && !thread->getSequence()->animatesScale())
|
||||
checkScaleCurrentlyAnimated();
|
||||
else if (!mScaleCurrentlyAnimated && thread->getSequence()->animatesScale())
|
||||
mScaleCurrentlyAnimated=true;
|
||||
|
||||
mTransitionRotationNodes.overlap(thread->transitionData.oldRotationNodes);
|
||||
mTransitionRotationNodes.overlap(thread->getSequence()->rotationMatters);
|
||||
|
||||
mTransitionTranslationNodes.overlap(thread->transitionData.oldTranslationNodes);
|
||||
mTransitionTranslationNodes.overlap(thread->getSequence()->translationMatters);
|
||||
|
||||
mTransitionScaleNodes.overlap(thread->transitionData.oldScaleNodes);
|
||||
mTransitionScaleNodes.overlap(thread->getSequence()->scaleMatters);
|
||||
|
||||
// if we aren't already in the list of transition threads, add us now
|
||||
S32 i;
|
||||
for (i=0; i<mTransitionThreads.size(); i++)
|
||||
if (mTransitionThreads[i]==thread)
|
||||
break;
|
||||
if (i==mTransitionThreads.size())
|
||||
mTransitionThreads.push_back(thread);
|
||||
|
||||
updateTransitions();
|
||||
}
|
||||
|
||||
void TSShapeInstance::clearTransition(TSThread * thread)
|
||||
{
|
||||
if (!thread->transitionData.inTransition)
|
||||
return;
|
||||
|
||||
// if other transitions are still playing,
|
||||
// make sure transforms are up to date
|
||||
if (mTransitionThreads.size()>1)
|
||||
animateNodeSubtrees();
|
||||
|
||||
// turn off transition...
|
||||
thread->transitionData.inTransition = false;
|
||||
|
||||
// remove us from transition list
|
||||
S32 i;
|
||||
if (mTransitionThreads.size() != 0) {
|
||||
for (i=0; i<mTransitionThreads.size(); i++)
|
||||
if (mTransitionThreads[i]==thread)
|
||||
break;
|
||||
AssertFatal(i!=mTransitionThreads.size(),"TSShapeInstance::clearTransition");
|
||||
mTransitionThreads.erase(i);
|
||||
}
|
||||
|
||||
// recompute transitionNodes
|
||||
mTransitionRotationNodes.clearAll();
|
||||
mTransitionTranslationNodes.clearAll();
|
||||
mTransitionScaleNodes.clearAll();
|
||||
for (i=0; i<mTransitionThreads.size(); i++)
|
||||
{
|
||||
mTransitionRotationNodes.overlap(mTransitionThreads[i]->transitionData.oldRotationNodes);
|
||||
mTransitionRotationNodes.overlap(mTransitionThreads[i]->getSequence()->rotationMatters);
|
||||
|
||||
mTransitionTranslationNodes.overlap(mTransitionThreads[i]->transitionData.oldTranslationNodes);
|
||||
mTransitionTranslationNodes.overlap(mTransitionThreads[i]->getSequence()->translationMatters);
|
||||
|
||||
mTransitionScaleNodes.overlap(mTransitionThreads[i]->transitionData.oldScaleNodes);
|
||||
mTransitionScaleNodes.overlap(mTransitionThreads[i]->getSequence()->scaleMatters);
|
||||
}
|
||||
|
||||
setDirty(ThreadDirty);
|
||||
|
||||
updateTransitions();
|
||||
}
|
||||
|
||||
void TSShapeInstance::updateTransitions()
|
||||
{
|
||||
if (mTransitionThreads.empty())
|
||||
return;
|
||||
|
||||
TSIntegerSet transitionNodes;
|
||||
updateTransitionNodeTransforms(transitionNodes);
|
||||
|
||||
S32 i;
|
||||
mNodeReferenceRotations.setSize(mShape->nodes.size());
|
||||
mNodeReferenceTranslations.setSize(mShape->nodes.size());
|
||||
for (i=0; i<mShape->nodes.size(); i++)
|
||||
{
|
||||
if (mTransitionRotationNodes.test(i))
|
||||
mNodeReferenceRotations[i].set(smNodeCurrentRotations[i]);
|
||||
if (mTransitionTranslationNodes.test(i))
|
||||
mNodeReferenceTranslations[i] = smNodeCurrentTranslations[i];
|
||||
}
|
||||
|
||||
if (animatesScale())
|
||||
{
|
||||
// Make sure smNodeXXXScale arrays have been resized
|
||||
TSIntegerSet dummySet;
|
||||
handleDefaultScale(0, 0, dummySet);
|
||||
|
||||
if (animatesUniformScale())
|
||||
{
|
||||
mNodeReferenceUniformScales.setSize(mShape->nodes.size());
|
||||
for (i=0; i<mShape->nodes.size(); i++)
|
||||
{
|
||||
if (mTransitionScaleNodes.test(i))
|
||||
mNodeReferenceUniformScales[i] = smNodeCurrentUniformScales[i];
|
||||
}
|
||||
}
|
||||
else if (animatesAlignedScale())
|
||||
{
|
||||
mNodeReferenceScaleFactors.setSize(mShape->nodes.size());
|
||||
for (i=0; i<mShape->nodes.size(); i++)
|
||||
{
|
||||
if (mTransitionScaleNodes.test(i))
|
||||
mNodeReferenceScaleFactors[i] = smNodeCurrentAlignedScales[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mNodeReferenceScaleFactors.setSize(mShape->nodes.size());
|
||||
mNodeReferenceArbitraryScaleRots.setSize(mShape->nodes.size());
|
||||
for (i=0; i<mShape->nodes.size(); i++)
|
||||
{
|
||||
if (mTransitionScaleNodes.test(i))
|
||||
{
|
||||
mNodeReferenceScaleFactors[i] = smNodeCurrentArbitraryScales[i].mScale;
|
||||
mNodeReferenceArbitraryScaleRots[i].set(smNodeCurrentArbitraryScales[i].mRotate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset transition durations to account for new reference transforms
|
||||
for (i=0; i<mTransitionThreads.size(); i++)
|
||||
{
|
||||
TSThread * th = mTransitionThreads[i];
|
||||
if (th->transitionData.inTransition)
|
||||
{
|
||||
th->transitionData.duration *= 1.0f - th->transitionData.pos;
|
||||
th->transitionData.pos = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeInstance::checkScaleCurrentlyAnimated()
|
||||
{
|
||||
mScaleCurrentlyAnimated=true;
|
||||
for (S32 i=0; i<mThreadList.size(); i++)
|
||||
if (mThreadList[i]->getSequence()->animatesScale())
|
||||
return;
|
||||
mScaleCurrentlyAnimated=false;
|
||||
}
|
||||
|
||||
void TSShapeInstance::setBlendEnabled(TSThread * thread, bool blendOn)
|
||||
{
|
||||
thread->blendDisabled = !blendOn;
|
||||
}
|
||||
|
||||
bool TSShapeInstance::getBlendEnabled(TSThread * thread)
|
||||
{
|
||||
return !thread->blendDisabled;
|
||||
}
|
||||
|
||||
void TSShapeInstance::setPriority(TSThread * thread, F32 priority)
|
||||
{
|
||||
thread->priority = priority;
|
||||
}
|
||||
|
||||
F32 TSShapeInstance::getPriority(TSThread * thread)
|
||||
{
|
||||
return thread->priority;
|
||||
}
|
||||
|
||||
F32 TSShapeInstance::getTime(TSThread * thread)
|
||||
{
|
||||
return thread->getTime();
|
||||
}
|
||||
|
||||
F32 TSShapeInstance::getPos(TSThread * thread)
|
||||
{
|
||||
return thread->getPos();
|
||||
}
|
||||
|
||||
void TSShapeInstance::setTime(TSThread * thread, F32 time)
|
||||
{
|
||||
thread->setTime(time);
|
||||
}
|
||||
|
||||
void TSShapeInstance::setPos(TSThread * thread, F32 pos)
|
||||
{
|
||||
thread->setPos(pos);
|
||||
}
|
||||
|
||||
bool TSShapeInstance::isInTransition(TSThread * thread)
|
||||
{
|
||||
return thread->isInTransition();
|
||||
}
|
||||
|
||||
F32 TSShapeInstance::getTimeScale(TSThread * thread)
|
||||
{
|
||||
return thread->getTimeScale();
|
||||
}
|
||||
|
||||
void TSShapeInstance::setTimeScale(TSThread * thread, F32 timeScale)
|
||||
{
|
||||
thread->setTimeScale(timeScale);
|
||||
}
|
||||
|
||||
F32 TSShapeInstance::getDuration(TSThread * thread)
|
||||
{
|
||||
return thread->getDuration();
|
||||
}
|
||||
|
||||
F32 TSShapeInstance::getScaledDuration(TSThread * thread)
|
||||
{
|
||||
return thread->getScaledDuration();
|
||||
}
|
||||
|
||||
S32 TSShapeInstance::getKeyframeCount(TSThread * thread)
|
||||
{
|
||||
return thread->getKeyframeCount();
|
||||
}
|
||||
|
||||
S32 TSShapeInstance::getKeyframeNumber(TSThread * thread)
|
||||
{
|
||||
return thread->getKeyframeNumber();
|
||||
}
|
||||
|
||||
void TSShapeInstance::setKeyframeNumber(TSThread * thread, S32 kf)
|
||||
{
|
||||
thread->setKeyframeNumber(kf);
|
||||
}
|
||||
|
||||
|
||||
// advance time on a particular thread
|
||||
void TSShapeInstance::advanceTime(F32 delta, TSThread * thread)
|
||||
{
|
||||
thread->advanceTime(delta);
|
||||
}
|
||||
|
||||
// advance time on all threads
|
||||
void TSShapeInstance::advanceTime(F32 delta)
|
||||
{
|
||||
for (S32 i=0; i<mThreadList.size(); i++)
|
||||
mThreadList[i]->advanceTime(delta);
|
||||
}
|
||||
|
||||
// advance pos on a particular thread
|
||||
void TSShapeInstance::advancePos(F32 delta, TSThread * thread)
|
||||
{
|
||||
thread->advancePos(delta);
|
||||
}
|
||||
|
||||
// advance pos on all threads
|
||||
void TSShapeInstance::advancePos(F32 delta)
|
||||
{
|
||||
for (S32 i=0; i<mThreadList.size(); i++)
|
||||
mThreadList[i]->advancePos(delta);
|
||||
}
|
||||
|
||||
156
Engine/source/ts/tsTransform.cpp
Normal file
156
Engine/source/ts/tsTransform.cpp
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "ts/tsTransform.h"
|
||||
#include "core/stream/stream.h"
|
||||
|
||||
void Quat16::identity()
|
||||
{
|
||||
x = y = z = 0;
|
||||
w = MAX_VAL;
|
||||
}
|
||||
|
||||
QuatF Quat16::getQuatF() const
|
||||
{
|
||||
return QuatF ( F32(x) / F32(MAX_VAL),
|
||||
F32(y) / F32(MAX_VAL),
|
||||
F32(z) / F32(MAX_VAL),
|
||||
F32(w) / F32(MAX_VAL) );
|
||||
}
|
||||
|
||||
QuatF & Quat16::getQuatF( QuatF * q ) const
|
||||
{
|
||||
q->x = F32( x ) / F32(MAX_VAL);
|
||||
q->y = F32( y ) / F32(MAX_VAL);
|
||||
q->z = F32( z ) / F32(MAX_VAL);
|
||||
q->w = F32( w ) / F32(MAX_VAL);
|
||||
return *q;
|
||||
}
|
||||
|
||||
void Quat16::set( const QuatF & q )
|
||||
{
|
||||
x = (S16)(q.x * F32(MAX_VAL));
|
||||
y = (S16)(q.y * F32(MAX_VAL));
|
||||
z = (S16)(q.z * F32(MAX_VAL));
|
||||
w = (S16)(q.w * F32(MAX_VAL));
|
||||
}
|
||||
|
||||
S32 Quat16::operator==( const Quat16 & q ) const
|
||||
{
|
||||
return( x == q.x && y == q.y && z == q.z && w == q.w );
|
||||
}
|
||||
|
||||
void Quat16::read(Stream * s)
|
||||
{
|
||||
s->read(&x);
|
||||
s->read(&y);
|
||||
s->read(&z);
|
||||
s->read(&w);
|
||||
}
|
||||
|
||||
void Quat16::write(Stream * s)
|
||||
{
|
||||
s->write(x);
|
||||
s->write(y);
|
||||
s->write(z);
|
||||
s->write(w);
|
||||
}
|
||||
|
||||
QuatF & TSTransform::interpolate( const QuatF & q1, const QuatF & q2, F32 interp, QuatF * q )
|
||||
{
|
||||
F32 Dot;
|
||||
F32 Dist2;
|
||||
F32 OneOverL;
|
||||
F32 x1,y1,z1,w1;
|
||||
F32 x2,y2,z2,w2;
|
||||
|
||||
//
|
||||
// This is a linear interpolation with a fast renormalization.
|
||||
//
|
||||
x1 = q1.x;
|
||||
y1 = q1.y;
|
||||
z1 = q1.z;
|
||||
w1 = q1.w;
|
||||
x2 = q2.x;
|
||||
y2 = q2.y;
|
||||
z2 = q2.z;
|
||||
w2 = q2.w;
|
||||
|
||||
// Determine if quats are further than 90 degrees
|
||||
Dot = x1*x2 + y1*y2 + z1*z2 + w1*w2;
|
||||
|
||||
// If dot is negative flip one of the quaterions
|
||||
if( Dot < 0.0f )
|
||||
{
|
||||
x1 = -x1;
|
||||
y1 = -y1;
|
||||
z1 = -z1;
|
||||
w1 = -w1;
|
||||
}
|
||||
|
||||
// Compute interpolated values
|
||||
x1 = x1 + interp*(x2 - x1);
|
||||
y1 = y1 + interp*(y2 - y1);
|
||||
z1 = z1 + interp*(z2 - z1);
|
||||
w1 = w1 + interp*(w2 - w1);
|
||||
|
||||
// Get squared distance of new quaternion
|
||||
Dist2 = x1*x1 + y1*y1 + z1*z1 + w1*w1;
|
||||
|
||||
// Use home-baked polynomial to compute 1/sqrt(Dist2)
|
||||
// since we know the range is 0.707 >= Dist2 <= 1.0
|
||||
// we'll split in half.
|
||||
|
||||
if( Dist2<0.857f )
|
||||
OneOverL = (((0.699368f)*Dist2) + -1.819985f)*Dist2 + 2.126369f; //0.0000792
|
||||
else
|
||||
OneOverL = (((0.454012f)*Dist2) + -1.403517f)*Dist2 + 1.949542f; //0.0000373
|
||||
|
||||
// Renormalize
|
||||
q->x = x1*OneOverL;
|
||||
q->y = y1*OneOverL;
|
||||
q->z = z1*OneOverL;
|
||||
q->w = w1*OneOverL;
|
||||
|
||||
return *q;
|
||||
}
|
||||
|
||||
void TSTransform::applyScale(F32 scale, MatrixF * mat)
|
||||
{
|
||||
mat->scale(Point3F(scale,scale,scale));
|
||||
}
|
||||
|
||||
void TSTransform::applyScale(const Point3F & scale, MatrixF * mat)
|
||||
{
|
||||
mat->scale(scale);
|
||||
}
|
||||
|
||||
void TSTransform::applyScale(const TSScale & scale, MatrixF * mat)
|
||||
{
|
||||
MatrixF mat2;
|
||||
TSTransform::setMatrix(scale.mRotate,&mat2);
|
||||
MatrixF mat3(mat2);
|
||||
mat3.inverse();
|
||||
mat2.scale(scale.mScale);
|
||||
mat2.mul(mat3);
|
||||
mat->mul(mat2);
|
||||
}
|
||||
109
Engine/source/ts/tsTransform.h
Normal file
109
Engine/source/ts/tsTransform.h
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSTRANSFORM_H_
|
||||
#define _TSTRANSFORM_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
|
||||
class Stream;
|
||||
|
||||
/// compressed quaternion class
|
||||
struct Quat16
|
||||
{
|
||||
enum { MAX_VAL = 0x7fff };
|
||||
|
||||
S16 x, y, z, w;
|
||||
|
||||
void read(Stream *);
|
||||
void write(Stream *);
|
||||
|
||||
void identity();
|
||||
QuatF getQuatF() const;
|
||||
QuatF &getQuatF( QuatF * q ) const;
|
||||
void set( const QuatF & q );
|
||||
S32 operator==( const Quat16 & q ) const;
|
||||
S32 operator!=( const Quat16 & q ) const { return !(*this == q); }
|
||||
};
|
||||
|
||||
/// class to handle general scaling case
|
||||
///
|
||||
/// transform = rot * scale * inv(rot)
|
||||
struct TSScale
|
||||
{
|
||||
QuatF mRotate;
|
||||
Point3F mScale;
|
||||
|
||||
void identity() { mRotate.identity(); mScale.set( 1.0f,1.0f,1.0f ); }
|
||||
S32 operator==( const TSScale & other ) const { return mRotate==other.mRotate && mScale==other.mScale; }
|
||||
};
|
||||
|
||||
/// struct for encapsulating ts transform related static functions
|
||||
struct TSTransform
|
||||
{
|
||||
static Point3F & interpolate(const Point3F & p1, const Point3F & p2, F32 t, Point3F *);
|
||||
static F32 interpolate(F32 p1, F32 p2, F32 t);
|
||||
static QuatF & interpolate(const QuatF & q1, const QuatF & q2, F32 t, QuatF *);
|
||||
static TSScale & interpolate(const TSScale & s1, const TSScale & s2, F32 t, TSScale *);
|
||||
|
||||
static void setMatrix(const QuatF & q, MatrixF *);
|
||||
static void setMatrix(const QuatF & q, const Point3F & p, MatrixF *);
|
||||
|
||||
static void applyScale(F32 scale, MatrixF *);
|
||||
static void applyScale(const Point3F & scale, MatrixF *);
|
||||
static void applyScale(const TSScale & scale, MatrixF *);
|
||||
};
|
||||
|
||||
inline Point3F & TSTransform::interpolate(const Point3F & p1, const Point3F & p2, F32 t, Point3F * p)
|
||||
{
|
||||
p->x = p1.x + t * (p2.x-p1.x);
|
||||
p->y = p1.y + t * (p2.y-p1.y);
|
||||
p->z = p1.z + t * (p2.z-p1.z);
|
||||
return *p;
|
||||
}
|
||||
|
||||
inline F32 TSTransform::interpolate(F32 p1, F32 p2, F32 t)
|
||||
{
|
||||
return p1 + t*(p2-p1);
|
||||
}
|
||||
|
||||
inline TSScale & TSTransform::interpolate(const TSScale & s1, const TSScale & s2, F32 t, TSScale * s)
|
||||
{
|
||||
TSTransform::interpolate(s1.mRotate,s2.mRotate,t,&s->mRotate);
|
||||
TSTransform::interpolate(s1.mScale,s2.mScale,t,&s->mScale);
|
||||
return *s;
|
||||
}
|
||||
|
||||
inline void TSTransform::setMatrix( const QuatF & q, const Point3F & p, MatrixF * pDest )
|
||||
{
|
||||
q.setMatrix(pDest);
|
||||
pDest->setColumn(3,p);
|
||||
}
|
||||
|
||||
inline void TSTransform::setMatrix( const QuatF & q, MatrixF * pDest )
|
||||
{
|
||||
q.setMatrix(pDest);
|
||||
}
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue