1983 lines
44 KiB
C++
1983 lines
44 KiB
C++
#include "fmod_geometryi.h"
|
|
|
|
#ifdef FMOD_SUPPORT_GEOMETRY
|
|
|
|
#include "fmod_3d.h"
|
|
#include "fmod_memory.h"
|
|
#include "fmod_geometry_mgr.h"
|
|
#include "fmod_systemi.h"
|
|
#include "fmod_localcriticalsection.h"
|
|
|
|
#include <string.h>
|
|
|
|
namespace FMOD
|
|
{
|
|
|
|
#if defined(_DEBUG) && defined(FMOD_GEOMETRY_DEBUGGING)
|
|
#define ASSERT(x) do { if (!(x)) __asm { int 3 } } while (false)
|
|
#else
|
|
#define ASSERT(x)
|
|
#endif
|
|
|
|
// if the sound transsmision drop this low then
|
|
// don't bother doing any more ray testing
|
|
const float MIN_TRANSMISSION = 0.05f;
|
|
|
|
static inline float MAX(float x, float y) { return x > y ? x : y; }
|
|
static inline float MIN(float x, float y) { return x < y ? x : y; }
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
static inline void cross(const FMOD_VECTOR& a, const FMOD_VECTOR& b, FMOD_VECTOR& result)
|
|
{
|
|
result.x = a.y * b.z - a.z * b.y;
|
|
result.y = a.z * b.x - a.x * b.z;
|
|
result.z = a.x * b.y - a.y * b.x;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
static void matrixMult(const float matrix[3][4], const FMOD_VECTOR *src, FMOD_VECTOR *dst)
|
|
{
|
|
dst->x = matrix[0][0] * src->x + matrix[0][1] * src->y + matrix[0][2] * src->z;
|
|
dst->y = matrix[1][0] * src->x + matrix[1][1] * src->y + matrix[1][2] * src->z;
|
|
dst->z = matrix[2][0] * src->x + matrix[2][1] * src->y + matrix[2][2] * src->z;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
void GeometryI::calculateMatrix()
|
|
{
|
|
mMatrix[1][0] = mUp.x * mScale.y;
|
|
mMatrix[1][1] = mUp.y * mScale.y;
|
|
mMatrix[1][2] = mUp.z * mScale.y;
|
|
mMatrix[2][0] = mForward.x * mScale.z;
|
|
mMatrix[2][1] = mForward.y * mScale.z;
|
|
mMatrix[2][2] = mForward.z * mScale.z;
|
|
mMatrix[0][0] = (mUp.y * mForward.z - mUp.z * mForward.y) * mScale.x;
|
|
mMatrix[0][1] = (mUp.z * mForward.x - mUp.x * mForward.z) * mScale.x;
|
|
mMatrix[0][2] = (mUp.x * mForward.y - mUp.y * mForward.x) * mScale.x;
|
|
|
|
mInvMatrix[0][1] = mUp.x / mScale.y;
|
|
mInvMatrix[1][1] = mUp.y / mScale.y;
|
|
mInvMatrix[2][1] = mUp.z / mScale.y;
|
|
mInvMatrix[0][2] = mForward.x / mScale.z;
|
|
mInvMatrix[1][2] = mForward.y / mScale.z;
|
|
mInvMatrix[2][2] = mForward.z / mScale.z;
|
|
mInvMatrix[0][0] = (mUp.y * mForward.z - mUp.z * mForward.y) / mScale.x;
|
|
mInvMatrix[1][0] = (mUp.z * mForward.x - mUp.x * mForward.z) / mScale.x;
|
|
mInvMatrix[2][0] = (mUp.x * mForward.y - mUp.y * mForward.x) / mScale.x;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
void GeometryI::updateSpatialData()
|
|
{
|
|
// find world aabb
|
|
FMOD_VECTOR center;
|
|
center.x = (mAABB.xMax + mAABB.xMin) * 0.5f;
|
|
center.y = (mAABB.yMax + mAABB.yMin) * 0.5f;
|
|
center.z = (mAABB.zMax + mAABB.zMin) * 0.5f;
|
|
FMOD_VECTOR newCenter;
|
|
matrixMult(mMatrix, ¢er, &newCenter);
|
|
newCenter.x += mPosition.x;
|
|
newCenter.y += mPosition.y;
|
|
newCenter.z += mPosition.z;
|
|
|
|
|
|
float xExtent = (mAABB.xMax - mAABB.xMin) * 0.5f;
|
|
float yExtent = (mAABB.yMax - mAABB.yMin) * 0.5f;
|
|
float zExtent = (mAABB.zMax - mAABB.zMin) * 0.5f;
|
|
|
|
float xNewExtent = (float)fabs(mMatrix[0][0]) * xExtent + (float)fabs(mMatrix[1][0]) * yExtent + (float)fabs(mMatrix[2][0]) * zExtent;
|
|
float yNewExtent = (float)fabs(mMatrix[0][1]) * xExtent + (float)fabs(mMatrix[1][1]) * yExtent + (float)fabs(mMatrix[2][1]) * zExtent;
|
|
float zNewExtent = (float)fabs(mMatrix[0][2]) * xExtent + (float)fabs(mMatrix[1][2]) * yExtent + (float)fabs(mMatrix[2][2]) * zExtent;
|
|
|
|
|
|
ASSERT(mSpatialData);
|
|
mSpatialData->octreeNode.aabb.xMax = newCenter.x + xNewExtent;
|
|
mSpatialData->octreeNode.aabb.xMin = newCenter.x - xNewExtent;
|
|
mSpatialData->octreeNode.aabb.yMax = newCenter.y + yNewExtent;
|
|
mSpatialData->octreeNode.aabb.yMin = newCenter.y - yNewExtent;
|
|
mSpatialData->octreeNode.aabb.zMax = newCenter.z + zNewExtent;
|
|
mSpatialData->octreeNode.aabb.zMin = newCenter.z - zNewExtent;
|
|
|
|
if (mActive)
|
|
{
|
|
mGeometryMgr->mainOctree()->updateItem(&mSpatialData->octreeNode);
|
|
}
|
|
else
|
|
{
|
|
mGeometryMgr->mainOctree()->deleteItem(&mSpatialData->octreeNode);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
void GeometryI::setToBeUpdated()
|
|
{
|
|
mGeometryMgr->mMoved = true;
|
|
|
|
if (!mToBeUpdated)
|
|
{
|
|
mToBeUpdated = true;
|
|
mNextUpdateItem = mGeometryMgr->mFirstUpdateItem;
|
|
mGeometryMgr->mFirstUpdateItem = this;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
bool GeometryI::octreeLineTestCallback(OctreeNode* item, void* data)
|
|
{
|
|
LineTestData *lineTestData = (LineTestData*)data;
|
|
|
|
ASSERT(lineTestData);
|
|
ASSERT(lineTestData->geometryI);
|
|
|
|
const FMOD_VECTOR& a = lineTestData->start;
|
|
const FMOD_VECTOR& b = lineTestData->end;
|
|
|
|
FMOD_POLYGON& polygon = *(FMOD_POLYGON*)item;
|
|
|
|
float dotA = FMOD_Vector_DotProduct(&polygon.normal, &a) - polygon.distance;
|
|
float dotB = FMOD_Vector_DotProduct(&polygon.normal, &b) - polygon.distance;
|
|
|
|
if (dotA >= 0.0f && dotB >= 0.0f || dotA <= 0.0f && dotB <= 0.0f)
|
|
{
|
|
return true; // no collision
|
|
}
|
|
|
|
if (dotA > 0.0f && (polygon.flags & FMOD_POLYGON_FLAG_DOUBLE_SIDED) == 0)
|
|
{
|
|
return true; // no collision
|
|
}
|
|
|
|
// find point on plane
|
|
float time = dotA / (dotA - dotB);
|
|
|
|
FMOD_VECTOR collisionPosition;
|
|
|
|
collisionPosition.x = a.x + (b.x - a.x) * time;
|
|
collisionPosition.y = a.y + (b.y - a.y) * time;
|
|
collisionPosition.z = a.z + (b.z - a.z) * time;
|
|
|
|
FMOD_VECTOR* vertices = &polygon.vertices;
|
|
|
|
// find if point lies inside all edges of polygon
|
|
int side;
|
|
for (side = 0; side < (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK); side++)
|
|
{
|
|
FMOD_VECTOR diff;
|
|
FMOD_VECTOR edgeVector;
|
|
int next = side + 1;
|
|
|
|
if (next >= (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK))
|
|
{
|
|
next = 0;
|
|
}
|
|
|
|
diff.x = vertices[next].x - vertices[side].x;
|
|
diff.y = vertices[next].y - vertices[side].y;
|
|
diff.z = vertices[next].z - vertices[side].z;
|
|
|
|
cross(diff, polygon.normal, edgeVector);
|
|
|
|
float dot =
|
|
edgeVector.x * (collisionPosition.x - vertices[side].x) +
|
|
edgeVector.y * (collisionPosition.y - vertices[side].y) +
|
|
edgeVector.z * (collisionPosition.z - vertices[side].z);
|
|
|
|
if (dot > 0.0f)
|
|
{
|
|
break; // point is outside polygon so no interestection occured
|
|
}
|
|
}
|
|
if (side == (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK))
|
|
{
|
|
if (lineTestData->geometryI->mGeometryMgr->mSystem->mFlags & FMOD_INIT_GEOMETRY_USECLOSEST)
|
|
{
|
|
float newDirectTransmission = (1.0f - polygon.directOcclusion);
|
|
float newReverbTransmission = (1.0f - polygon.reverbOcclusion);
|
|
|
|
if (newDirectTransmission < lineTestData->directTransmission || (newDirectTransmission == lineTestData->directTransmission && newReverbTransmission < lineTestData->reverbTransmission))
|
|
{
|
|
lineTestData->directTransmission = newDirectTransmission;
|
|
lineTestData->reverbTransmission = newReverbTransmission;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lineTestData->directTransmission *= (1.0f - polygon.directOcclusion);
|
|
lineTestData->reverbTransmission *= (1.0f - polygon.reverbOcclusion);
|
|
}
|
|
|
|
#if defined(FMOD_GEOMETRY_DEBUGGING)
|
|
// for function testLineTestForEachPolygon to see if this
|
|
// polygon has actually been intersected with
|
|
polygon.flags &= ~0x80000000;
|
|
#else
|
|
if (lineTestData->directTransmission < MIN_TRANSMISSION &&
|
|
lineTestData->reverbTransmission < MIN_TRANSMISSION)
|
|
{
|
|
// sounds level has dropped too low so stop doing line testing
|
|
return false;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
GeometryI::GeometryI(GeometryMgr* geometryMgr) : LinkedListNode(), mOctree(geometryMgr->getWorldSize())
|
|
{
|
|
mGeometryMgr = geometryMgr;
|
|
mMaxNumVertices = 0;
|
|
mNumVertices = 0;
|
|
mMaxNumPolygons = 0;
|
|
mNumPolygons = 0;
|
|
mPolygonOffsets = 0;
|
|
mPolygonDataPos = 0;
|
|
mPolygonData = 0;
|
|
|
|
mForward.x = 0.0f;
|
|
mForward.y = 0.0f;
|
|
mForward.z = 1.0f;
|
|
mUp.x = 0.0f;
|
|
mUp.y = 1.0f;
|
|
mUp.z = 0.0f;
|
|
mPosition.x = 0.0f;
|
|
mPosition.y = 0.0f;
|
|
mPosition.z = 0.0f;
|
|
mScale.x = 1.0f;
|
|
mScale.y = 1.0f;
|
|
mScale.z = 1.0f;
|
|
calculateMatrix();
|
|
|
|
mPolygonUpdateList = 0;
|
|
mNextUpdateItem = 0;
|
|
mToBeUpdated = false;
|
|
mActive = true;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::release()
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true);
|
|
#endif
|
|
// mGeometryMgr->flushAll();
|
|
|
|
// find out if we are in the "to be updated" list and remove us.
|
|
// Other wise we will try to update a deleted object
|
|
GeometryI* geometryI = mGeometryMgr->mFirstUpdateItem;
|
|
GeometryI* prevGeometryI = 0;
|
|
while (geometryI)
|
|
{
|
|
if (geometryI == this)
|
|
{
|
|
if (prevGeometryI)
|
|
{
|
|
prevGeometryI->mNextUpdateItem = geometryI->mNextUpdateItem;
|
|
}
|
|
else
|
|
{
|
|
mGeometryMgr->mFirstUpdateItem = geometryI->mNextUpdateItem;
|
|
}
|
|
break;
|
|
}
|
|
prevGeometryI = geometryI;
|
|
geometryI = geometryI->mNextUpdateItem;
|
|
}
|
|
|
|
|
|
if (mSpatialData)
|
|
{
|
|
mGeometryMgr->mainOctree()->deleteItem(&mSpatialData->octreeNode);
|
|
mGeometryMgr->mainOctree()->removeInternalNode(&mSpatialData->octreeInternalNode);
|
|
mGeometryMgr->releaseMainOctree();
|
|
FMOD_Memory_Free(mSpatialData);
|
|
mSpatialData = 0;
|
|
}
|
|
if (mPolygonData)
|
|
{
|
|
FMOD_Memory_Free(mPolygonData);
|
|
mPolygonData = 0;
|
|
}
|
|
if (mPolygonOffsets)
|
|
{
|
|
FMOD_Memory_Free(mPolygonOffsets);
|
|
mPolygonOffsets = 0;
|
|
}
|
|
|
|
mGeometryMgr->mMoved = true; // Make it reset the voices back to unoccluded.
|
|
mGeometryMgr->mSystem->update();
|
|
|
|
if (mGeometryMgr->mSystem->mGeometryList == this)
|
|
{
|
|
mGeometryMgr->mSystem->mGeometryList = (GeometryI*)getNext();
|
|
if (mGeometryMgr->mSystem->mGeometryList == this)
|
|
{
|
|
mGeometryMgr->mSystem->mGeometryList = 0;
|
|
}
|
|
}
|
|
removeNode();
|
|
|
|
FMOD_Memory_Free(this);
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::alloc(int maxNumPolygons, int maxNumVertices)
|
|
{
|
|
FMOD_RESULT result;
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true);
|
|
#endif
|
|
|
|
if (mPolygonData || mPolygonOffsets )
|
|
{
|
|
return FMOD_ERR_INTERNAL;
|
|
}
|
|
|
|
mMaxNumVertices = maxNumVertices;
|
|
mNumVertices = 0;
|
|
mMaxNumPolygons = maxNumPolygons;
|
|
mNumPolygons = 0;
|
|
mPolygonOffsets = (int*)FMOD_Memory_Alloc(mMaxNumVertices * sizeof (int));
|
|
if (!mPolygonOffsets)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
mPolygonDataPos = 0;
|
|
int polygonDataSize = (sizeof(FMOD_POLYGON) - sizeof (FMOD_VECTOR)) * maxNumPolygons;
|
|
polygonDataSize += sizeof (FMOD_VECTOR) * maxNumVertices;
|
|
|
|
mPolygonData = (unsigned char*)FMOD_Memory_Alloc(polygonDataSize);
|
|
if (!mPolygonData)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
result = mGeometryMgr->aquireMainOctree();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
mSpatialData = (SpatialData *)FMOD_Memory_Alloc(sizeof (SpatialData));
|
|
if (!mSpatialData)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
FMOD_memset(mSpatialData, 0, sizeof (SpatialData));
|
|
mSpatialData->geometry = this;
|
|
|
|
mGeometryMgr->mainOctree()->addInternalNode(&mSpatialData->octreeInternalNode);
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::addPolygon(float directOcclusion, float reverbOcclusion, bool doubleSided, int numVertices, const FMOD_VECTOR *vertices, int *polygonIndex)
|
|
{
|
|
ASSERT(vertices);
|
|
ASSERT(numVertices >= 3);
|
|
ASSERT(mNumPolygons < mMaxNumPolygons);
|
|
ASSERT(mNumVertices + numVertices <= mMaxNumVertices);
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true);
|
|
#endif
|
|
|
|
if (vertices == 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (numVertices < 3)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (mNumPolygons >= mMaxNumPolygons)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (mNumVertices + numVertices > mMaxNumVertices)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (polygonIndex)
|
|
{
|
|
*polygonIndex = mNumPolygons;
|
|
}
|
|
|
|
mNumVertices += numVertices;
|
|
mPolygonOffsets[mNumPolygons] = mPolygonDataPos;
|
|
FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonDataPos];
|
|
mPolygonDataPos += sizeof(FMOD_POLYGON) + (numVertices - 1) * sizeof (FMOD_VECTOR);
|
|
mNumPolygons++;
|
|
|
|
FMOD_memset(&polygon.node, 0, sizeof (OctreeNode));
|
|
FMOD_memset(&polygon.nodeInternal, 0, sizeof (OctreeNode));
|
|
polygon.directOcclusion = directOcclusion;
|
|
polygon.reverbOcclusion = reverbOcclusion;
|
|
polygon.flags = numVertices;
|
|
if (doubleSided)
|
|
polygon.flags |= FMOD_POLYGON_FLAG_DOUBLE_SIDED;
|
|
|
|
int vertex;
|
|
for (vertex = 0; vertex < (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK); vertex++)
|
|
(&polygon.vertices)[vertex] = vertices[vertex];
|
|
|
|
// add to update list
|
|
polygon.node.nextItem = mPolygonUpdateList;
|
|
mPolygonUpdateList = &polygon.node;
|
|
|
|
setToBeUpdated();
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::getNumPolygons(int *numPolygons)
|
|
{
|
|
if (!numPolygons)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*numPolygons = mNumPolygons;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::getMaxPolygons(int *maxNumPolygons, int *maxVertices)
|
|
{
|
|
if (maxNumPolygons)
|
|
{
|
|
*maxNumPolygons = mMaxNumPolygons;
|
|
}
|
|
if (maxVertices)
|
|
{
|
|
*maxVertices = mMaxNumVertices;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::getPolygonNumVertices (int polygonIndex, int *numVertices)
|
|
{
|
|
ASSERT(polygonIndex >= 0);
|
|
ASSERT(polygonIndex < mNumPolygons);
|
|
ASSERT(mPolygonOffsets[polygonIndex] >= 0);
|
|
ASSERT(mPolygonOffsets[polygonIndex] < mPolygonDataPos);
|
|
|
|
if (polygonIndex < 0 || polygonIndex >= mNumPolygons)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[polygonIndex]];
|
|
|
|
if (numVertices)
|
|
{
|
|
*numVertices = (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK);
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::setPolygonVertex(int polygonIndex, int vertexIndex, const FMOD_VECTOR *vertex)
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true);
|
|
#endif
|
|
ASSERT(polygonIndex >= 0);
|
|
ASSERT(polygonIndex < mNumPolygons);
|
|
ASSERT(mPolygonOffsets[polygonIndex] >= 0);
|
|
ASSERT(mPolygonOffsets[polygonIndex] < mPolygonDataPos);
|
|
|
|
if (polygonIndex < 0 || polygonIndex >= mNumPolygons)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[polygonIndex]];
|
|
ASSERT(vertexIndex >= 0);
|
|
ASSERT(vertexIndex < (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK));
|
|
ASSERT(vertex);
|
|
|
|
if (vertexIndex < 0 || vertexIndex >= (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK))
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (vertex == 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if ((&polygon.vertices)[vertexIndex].x == vertex->x &&
|
|
(&polygon.vertices)[vertexIndex].y == vertex->y &&
|
|
(&polygon.vertices)[vertexIndex].z == vertex->z)
|
|
{
|
|
return FMOD_OK;
|
|
}
|
|
|
|
(&polygon.vertices)[vertexIndex] = *vertex;
|
|
|
|
// if we are in the octreee then remove and add to the update list
|
|
if (polygon.node.flags & OCTREE_FLAG_INSERTED)
|
|
{
|
|
mOctree.deleteItem(&polygon.node);
|
|
polygon.node.nextItem = mPolygonUpdateList;
|
|
mPolygonUpdateList = &polygon.node;
|
|
}
|
|
|
|
setToBeUpdated();
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::getPolygonVertex(int polygonIndex, int vertexIndex, FMOD_VECTOR *vertex)
|
|
{
|
|
ASSERT(polygonIndex >= 0);
|
|
ASSERT(polygonIndex < mNumPolygons);
|
|
ASSERT(mPolygonOffsets[polygonIndex] >= 0);
|
|
ASSERT(mPolygonOffsets[polygonIndex] < mPolygonDataPos);
|
|
|
|
if (polygonIndex < 0 || polygonIndex >= mNumPolygons || !vertex)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[polygonIndex]];
|
|
ASSERT(vertexIndex >= 0);
|
|
ASSERT(vertexIndex < (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK));
|
|
|
|
if (vertexIndex < 0 || vertexIndex >= (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK))
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*vertex = (&polygon.vertices)[vertexIndex];
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::setPolygonAttributes(int polygonIndex, float directOcclusion, float reverbOcclusion, bool doubleSided)
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true);
|
|
#endif
|
|
ASSERT(polygonIndex >= 0);
|
|
ASSERT(polygonIndex < mNumPolygons);
|
|
ASSERT(mPolygonOffsets[polygonIndex] >= 0);
|
|
ASSERT(mPolygonOffsets[polygonIndex] < mPolygonDataPos);
|
|
if (polygonIndex < 0 || polygonIndex >= mNumPolygons)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[polygonIndex]];
|
|
polygon.directOcclusion = directOcclusion;
|
|
polygon.reverbOcclusion = reverbOcclusion;
|
|
if (doubleSided)
|
|
{
|
|
polygon.flags |= FMOD_POLYGON_FLAG_DOUBLE_SIDED;
|
|
}
|
|
else
|
|
{
|
|
polygon.flags &= ~FMOD_POLYGON_FLAG_DOUBLE_SIDED;
|
|
}
|
|
|
|
setToBeUpdated();
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::getPolygonAttributes(int polygonIndex, float *directOcclusion, float *reverbOcclusion, bool *doubleSided)
|
|
{
|
|
ASSERT(polygonIndex >= 0);
|
|
ASSERT(polygonIndex < mNumPolygons);
|
|
ASSERT(mPolygonOffsets[polygonIndex] >= 0);
|
|
ASSERT(mPolygonOffsets[polygonIndex] < mPolygonDataPos);
|
|
|
|
if (polygonIndex < 0 || polygonIndex >= mNumPolygons)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true);
|
|
#endif
|
|
FMOD_POLYGON &polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[polygonIndex]];
|
|
|
|
if (directOcclusion)
|
|
{
|
|
*directOcclusion = polygon.directOcclusion;
|
|
}
|
|
if (reverbOcclusion)
|
|
{
|
|
*reverbOcclusion = polygon.reverbOcclusion;
|
|
}
|
|
if (doubleSided)
|
|
{
|
|
*doubleSided = (polygon.flags & FMOD_POLYGON_FLAG_DOUBLE_SIDED) != 0;
|
|
}
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::flush()
|
|
{
|
|
/*
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true);
|
|
#endif
|
|
*/
|
|
int vertex;
|
|
|
|
// iterate through polygons in the update list, calucate normals, aabb and add to octree.
|
|
OctreeNode* node = mPolygonUpdateList;
|
|
mPolygonUpdateList = 0;
|
|
while (node)
|
|
{
|
|
OctreeNode* next = node->nextItem;
|
|
node->nextItem = 0;
|
|
|
|
FMOD_POLYGON& polygon = *(FMOD_POLYGON*)node;
|
|
FMOD_VECTOR* vertices =& polygon.vertices;
|
|
|
|
// calculate polygon normal
|
|
float xN = 0.0f;
|
|
float yN = 0.0f;
|
|
float zN = 0.0f;
|
|
// todo: return an error if a polygon has less then 3 vertices.
|
|
for (vertex = 0; vertex < (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK) - 2; vertex++)
|
|
{
|
|
float xA = vertices[vertex + 1].x -vertices[0].x;
|
|
float yA = vertices[vertex + 1].y -vertices[0].y;
|
|
float zA = vertices[vertex + 1].z -vertices[0].z;
|
|
float xB = vertices[vertex + 2].x -vertices[0].x;
|
|
float yB = vertices[vertex + 2].y -vertices[0].y;
|
|
float zB = vertices[vertex + 2].z -vertices[0].z;
|
|
// cross product
|
|
xN += yA * zB - zA * yB;
|
|
yN += zA * xB - xA * zB;
|
|
zN += xA * yB - yA * xB;
|
|
}
|
|
float fMagnidued = (float)sqrt(xN * xN + yN * yN + zN * zN);
|
|
if (fMagnidued > 0.0f) // a tollerance here might be called for
|
|
{
|
|
polygon.flags &= ~FMOD_POLYGON_FLAG_INVALID;
|
|
xN /= fMagnidued;
|
|
yN /= fMagnidued;
|
|
zN /= fMagnidued;
|
|
}
|
|
else
|
|
{
|
|
polygon.flags |= FMOD_POLYGON_FLAG_INVALID;
|
|
}
|
|
polygon.normal.x = xN;
|
|
polygon.normal.y = yN;
|
|
polygon.normal.z = zN;
|
|
polygon.distance = FMOD_Vector_DotProduct(&vertices[0], &polygon.normal);
|
|
|
|
// add to octree
|
|
mOctree.addInternalNode(&polygon.nodeInternal);
|
|
FMOD_AABB& aabb = polygon.node.aabb;
|
|
aabb.xMax = aabb.xMin = vertices[0].x;
|
|
aabb.yMax = aabb.yMin = vertices[0].y;
|
|
aabb.zMax = aabb.zMin = vertices[0].z;
|
|
for (vertex = 1; vertex < (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK); vertex++)
|
|
{
|
|
aabb.xMax = MAX(aabb.xMax, vertices[vertex].x);
|
|
aabb.xMin = MIN(aabb.xMin, vertices[vertex].x);
|
|
aabb.yMax = MAX(aabb.yMax, vertices[vertex].y);
|
|
aabb.yMin = MIN(aabb.yMin, vertices[vertex].y);
|
|
aabb.zMax = MAX(aabb.zMax, vertices[vertex].z);
|
|
aabb.zMin = MIN(aabb.zMin, vertices[vertex].z);
|
|
}
|
|
// we need to grow the bounding box a little to cope with the
|
|
// case of an axis alligned polygon that might be right on the edge of a
|
|
// bounding box.
|
|
// Becaouse the line is clamped to each box in the tree before testing against
|
|
// the polygon, we need to make the boxes a little bit bigger then the
|
|
// polygons to make sure the line still passes through them.
|
|
float fTollerance = aabb.xMax - aabb.xMin;
|
|
fTollerance = MAX(fTollerance, aabb.yMax - aabb.yMin);
|
|
fTollerance = MAX(fTollerance, aabb.zMax - aabb.zMin);
|
|
fTollerance *= 0.01f;
|
|
aabb.xMin -= fTollerance;
|
|
aabb.xMax += fTollerance;
|
|
aabb.yMin -= fTollerance;
|
|
aabb.yMax += fTollerance;
|
|
aabb.zMin -= fTollerance;
|
|
aabb.zMax += fTollerance;
|
|
|
|
if ((polygon.flags & FMOD_POLYGON_FLAG_INVALID) == 0)
|
|
{
|
|
mOctree.insertItem(&polygon.node);
|
|
}
|
|
node = next;
|
|
}
|
|
|
|
// update spacial data
|
|
//mOctree.calculateAverageNodeElements(); // just for testing
|
|
mOctree.getAABB(&mAABB);
|
|
updateSpatialData();
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::setActive(bool active)
|
|
{
|
|
setToBeUpdated();
|
|
|
|
mActive = active;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::getActive(bool *active)
|
|
{
|
|
if (!active)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*active = mActive;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::setRotation(const FMOD_VECTOR* forward, const FMOD_VECTOR* up)
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true);
|
|
#endif
|
|
|
|
if (!forward)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (!up)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (mForward.x == forward->x &&
|
|
mForward.y == forward->y &&
|
|
mForward.z == forward->z &&
|
|
mUp.x == up->x &&
|
|
mUp.y == up->y &&
|
|
mUp.z == up->z)
|
|
{
|
|
return FMOD_OK;
|
|
}
|
|
|
|
mForward = *forward;
|
|
mUp = *up;
|
|
calculateMatrix();
|
|
setToBeUpdated();
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::getRotation(FMOD_VECTOR* forward, FMOD_VECTOR* up)
|
|
{
|
|
if (forward)
|
|
{
|
|
*forward = mForward;
|
|
}
|
|
|
|
if (up)
|
|
{
|
|
*up = mUp;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::setPosition(const FMOD_VECTOR* position)
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true);
|
|
#endif
|
|
|
|
if (!position)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (mPosition.x == position->x && mPosition.y == position->y && mPosition.z == position->z)
|
|
{
|
|
return FMOD_OK;
|
|
}
|
|
|
|
mPosition = *position;
|
|
|
|
setToBeUpdated();
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::getPosition(FMOD_VECTOR* position)
|
|
{
|
|
if (!position)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*position = mPosition;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::setScale(const FMOD_VECTOR* scale)
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true);
|
|
#endif
|
|
|
|
if (!scale)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (scale->x == 0.0f || scale->y == 0.0f || scale->z == 0.0f)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (mScale.x == scale->x &&
|
|
mScale.y == scale->y &&
|
|
mScale.z == scale->z)
|
|
{
|
|
return FMOD_OK;
|
|
}
|
|
mScale = *scale;
|
|
calculateMatrix();
|
|
setToBeUpdated();
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::getScale(FMOD_VECTOR *scale)
|
|
{
|
|
if (!scale)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*scale = mScale;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::save(void *data, int *dataSize)
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
|
|
if (!dataSize)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (data)
|
|
{
|
|
int tmpDataSize = *dataSize;
|
|
result = serialiser(data, &tmpDataSize, true, false, saveData);
|
|
}
|
|
else
|
|
{
|
|
result = serialiser(data, dataSize, false, false, countData);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::load(const void *data, int dataSize)
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true);
|
|
#endif
|
|
|
|
if (!data)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
return serialiser((void*)data, &dataSize, false, true, loadData);
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::saveData(void* fileData, int dataSize, int* fileDataIndex, void* liveData, int liveDataSize)
|
|
{
|
|
if (*fileDataIndex + liveDataSize > dataSize)
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
|
|
#ifdef PLATFORM_ENDIAN_BIG
|
|
if (liveDataSize == 4)
|
|
{
|
|
*(unsigned int*)&((unsigned char*)fileData)[*fileDataIndex] = *(unsigned int*)liveData;
|
|
}
|
|
else
|
|
{
|
|
FMOD_memcpy(&((unsigned char*)fileData)[*fileDataIndex], liveData, liveDataSize);
|
|
}
|
|
#else
|
|
FMOD_memcpy(&((unsigned char*)fileData)[*fileDataIndex], liveData, liveDataSize);
|
|
#endif
|
|
|
|
*fileDataIndex += liveDataSize;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::loadData(void* fileData, int dataSize, int* fileDataIndex, void* liveData, int liveDataSize)
|
|
{
|
|
if (*fileDataIndex + liveDataSize > dataSize)
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
|
|
#ifdef PLATFORM_ENDIAN_BIG
|
|
if (liveDataSize == 4)
|
|
{
|
|
*(unsigned int*)liveData = *(unsigned int*)&((unsigned char*)fileData)[*fileDataIndex];
|
|
}
|
|
else
|
|
{
|
|
FMOD_memcpy(liveData, &((unsigned char*)fileData)[*fileDataIndex], liveDataSize);
|
|
}
|
|
#else
|
|
FMOD_memcpy(liveData, &((unsigned char*)fileData)[*fileDataIndex], liveDataSize);
|
|
#endif
|
|
|
|
*fileDataIndex += liveDataSize;
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::countData(void* fileData, int dataSize, int* fileDataIndex, void* liveData, int liveDataSize)
|
|
{
|
|
*fileDataIndex += liveDataSize;
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::serialiser(
|
|
void *data,
|
|
int *dataSize,
|
|
bool bWrite,
|
|
bool bRead,
|
|
FMOD_RESULT (*serialiseData)(void* fileData, int dataSize, int* fileDataIndex, void* liveData, int liveDataSize))
|
|
{
|
|
bool bCount = !bWrite && !bRead;
|
|
|
|
#define _CHECK_RESULT(x) \
|
|
{ \
|
|
FMOD_RESULT result = (x); \
|
|
if (result != FMOD_OK) \
|
|
{ \
|
|
if (vertexArray) \
|
|
FMOD_Memory_Free(vertexArray); \
|
|
return result; \
|
|
} \
|
|
}
|
|
|
|
FMOD_VECTOR* vertexArray = 0;
|
|
int index = 0;
|
|
|
|
unsigned int header;
|
|
const unsigned int realHeader = 'F' + ('M' << 8) + ('O' << 16) + ('D' << 24);
|
|
header = realHeader;
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &header, sizeof (int)))
|
|
if (header != realHeader)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
int dataSizeCheck = *dataSize;
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &dataSizeCheck, sizeof (int)))
|
|
if (bRead)
|
|
{
|
|
if (*dataSize != dataSizeCheck)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
}
|
|
|
|
int numPolygons;
|
|
if (bWrite || bCount)
|
|
{
|
|
_CHECK_RESULT(getNumPolygons(&numPolygons))
|
|
}
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &numPolygons, sizeof (int)))
|
|
|
|
int maxPolygons;
|
|
int maxVertices;
|
|
if (bWrite || bCount)
|
|
{
|
|
_CHECK_RESULT(getMaxPolygons(&maxPolygons, &maxVertices))
|
|
}
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &maxPolygons, sizeof (int)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &maxVertices, sizeof (int)))
|
|
|
|
int maxNumVerticesInOnPolygon = 64; // not very likely that this will be broken
|
|
vertexArray = (FMOD_VECTOR*)FMOD_Memory_Alloc(maxNumVerticesInOnPolygon * sizeof (FMOD_VECTOR));
|
|
if (!vertexArray)
|
|
return FMOD_ERR_MEMORY;
|
|
|
|
if (bRead)
|
|
{
|
|
_CHECK_RESULT(alloc(maxPolygons, maxVertices))
|
|
}
|
|
for (int polygonIndex = 0; polygonIndex < numPolygons; polygonIndex++)
|
|
{
|
|
int numVertices;
|
|
if (bWrite || bCount)
|
|
{
|
|
_CHECK_RESULT(getPolygonNumVertices(polygonIndex, &numVertices))
|
|
}
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &numVertices, sizeof (int)))
|
|
|
|
if (maxNumVerticesInOnPolygon < numVertices)
|
|
{
|
|
// in the unlikly event we have more then 64 vertices in one polygon we need to
|
|
// reallocate the array
|
|
FMOD_Memory_Free(vertexArray);
|
|
vertexArray = (FMOD_VECTOR*)FMOD_Memory_Alloc(maxNumVerticesInOnPolygon * sizeof (FMOD_VECTOR));
|
|
if (!vertexArray)
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
for (int vertexIndex = 0; vertexIndex < numVertices; vertexIndex++)
|
|
{
|
|
// FMOD_VECTOR vertex = FMOD_Memory_Alloc(mMaxNumVertices * sizeof (int));
|
|
if (bWrite)
|
|
{
|
|
_CHECK_RESULT(getPolygonVertex(polygonIndex, vertexIndex, &vertexArray[vertexIndex]))
|
|
}
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &vertexArray[vertexIndex].x, sizeof (float)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &vertexArray[vertexIndex].y, sizeof (float)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &vertexArray[vertexIndex].z, sizeof (float)))
|
|
}
|
|
|
|
float directOcclusion;
|
|
float reverbOcclusion;
|
|
bool bDoubleSided = false;;
|
|
if (bWrite || bCount)
|
|
{
|
|
_CHECK_RESULT(getPolygonAttributes(polygonIndex, &directOcclusion, &reverbOcclusion, &bDoubleSided))
|
|
}
|
|
int doubleSided = bDoubleSided ? 1 : 0;
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &directOcclusion, sizeof (int)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &reverbOcclusion, sizeof (int)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &doubleSided, sizeof (int)))
|
|
bDoubleSided = doubleSided != 0;
|
|
|
|
if (bRead)
|
|
{
|
|
_CHECK_RESULT(addPolygon(directOcclusion, reverbOcclusion, bDoubleSided, numVertices, vertexArray, 0))
|
|
}
|
|
}
|
|
|
|
FMOD_Memory_Free(vertexArray);
|
|
vertexArray = 0;
|
|
|
|
FMOD_VECTOR forward;
|
|
FMOD_VECTOR up;
|
|
if (bWrite || bCount)
|
|
{
|
|
_CHECK_RESULT(getRotation(&forward, &up))
|
|
}
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &forward.x, sizeof (float)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &forward.y, sizeof (float)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &forward.z, sizeof (float)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &up.x, sizeof (float)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &up.y, sizeof (float)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &up.z, sizeof (float)))
|
|
if (bRead)
|
|
{
|
|
_CHECK_RESULT(setRotation(&forward, &up))
|
|
}
|
|
|
|
FMOD_VECTOR position;
|
|
if (bWrite || bCount)
|
|
{
|
|
_CHECK_RESULT(getPosition(&position))
|
|
}
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &position.x, sizeof (float)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &position.y, sizeof (float)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &position.z, sizeof (float)))
|
|
if (bRead)
|
|
{
|
|
_CHECK_RESULT(setPosition(&position))
|
|
}
|
|
|
|
FMOD_VECTOR scale;
|
|
if (bWrite || bCount)
|
|
{
|
|
_CHECK_RESULT(getScale(&scale))
|
|
}
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &scale.x, sizeof (float)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &scale.y, sizeof (float)))
|
|
_CHECK_RESULT(serialiseData(data, *dataSize, &index, &scale.z, sizeof (float)))
|
|
if (bRead)
|
|
{
|
|
_CHECK_RESULT(setScale(&scale))
|
|
}
|
|
|
|
#undef _CHECK_RESULT
|
|
|
|
if (bRead || bWrite)
|
|
{
|
|
if (*dataSize != index)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*dataSize = index;
|
|
}
|
|
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::setUserData(void *userdata)
|
|
{
|
|
mUserData = userdata;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::getUserData(void **userdata)
|
|
{
|
|
if (!userdata)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*userdata = mUserData;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
bool GeometryI::lineTest(LineTestData* lineTestData)
|
|
{
|
|
FMOD_VECTOR startBck = lineTestData->start;
|
|
FMOD_VECTOR endBck = lineTestData->end;
|
|
|
|
FMOD_VECTOR startTmp = lineTestData->start;
|
|
startTmp.x -= mPosition.x;
|
|
startTmp.y -= mPosition.y;
|
|
startTmp.z -= mPosition.z;
|
|
FMOD_VECTOR endTmp = lineTestData->end;
|
|
endTmp.x -= mPosition.x;
|
|
endTmp.y -= mPosition.y;
|
|
endTmp.z -= mPosition.z;
|
|
|
|
matrixMult(mInvMatrix, &startTmp, &lineTestData->start);
|
|
matrixMult(mInvMatrix, &endTmp, &lineTestData->end);
|
|
|
|
bool bResult = mOctree.testLine(octreeLineTestCallback, lineTestData, lineTestData->start, lineTestData->end);
|
|
|
|
lineTestData->start = startBck;
|
|
lineTestData->end = endBck;
|
|
|
|
lineTestData->geometryI = 0;
|
|
return bResult;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::setWorldSize(float worldSize)
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true);
|
|
#endif
|
|
mOctree.setMaxSize(worldSize);
|
|
int poly;
|
|
for (poly = 0; poly < mNumPolygons; poly++)
|
|
{
|
|
FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[poly]];
|
|
mOctree.deleteItem(&polygon.node);
|
|
}
|
|
for (poly = 0; poly < mNumPolygons; poly++)
|
|
{
|
|
FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[poly]];
|
|
polygon.node.nextItem = mPolygonUpdateList;
|
|
mPolygonUpdateList = &polygon.node;
|
|
}
|
|
setToBeUpdated();
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
void GeometryI::removeFromTree()
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
LocalCriticalSection crit(mGeometryMgr->mGeometryCrit, true);
|
|
#endif
|
|
mGeometryMgr->mainOctree()->deleteItem(&mSpatialData->octreeNode);
|
|
}
|
|
|
|
|
|
#if defined(FMOD_GEOMETRY_DEBUGGING)
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
void GeometryI::testLineTestForEachPolygon()
|
|
{
|
|
for (int poly = 0; poly < mNumPolygons; poly++)
|
|
{
|
|
FMOD_POLYGON& polygon = *(FMOD_POLYGON*)&mPolygonData[mPolygonOffsets[poly]];
|
|
if (polygon.flags & FMOD_POLYGON_FLAG_INVALID)
|
|
continue;
|
|
FMOD_VECTOR center = { 0.0f, 0.0f, 0.0f };
|
|
int numVertices = (polygon.flags & FMOD_POLYGON_NUM_VERTICES_MASK);
|
|
ASSERT(numVertices >= 3);
|
|
for (int i = 0; i < numVertices; i++)
|
|
{
|
|
center.x += (&polygon.vertices)[i].x;
|
|
center.y += (&polygon.vertices)[i].y;
|
|
center.z += (&polygon.vertices)[i].z;
|
|
}
|
|
center.x /= numVertices;
|
|
center.y /= numVertices;
|
|
center.z /= numVertices;
|
|
FMOD_VECTOR start;
|
|
start.x = center.x - polygon.normal.x * 100.0f;
|
|
start.y = center.y - polygon.normal.y * 100.0f;
|
|
start.z = center.z - polygon.normal.z * 100.0f;
|
|
FMOD_VECTOR end;
|
|
end.x = center.x + polygon.normal.x * 100.0f;
|
|
end.y = center.y + polygon.normal.y * 100.0f;
|
|
end.z = center.z + polygon.normal.z * 100.0f;
|
|
polygon.flags |= 0x80000000;
|
|
float a, b;
|
|
mGeometryMgr->lineTestAll(&start, &end, &a, &b);
|
|
ASSERT((polygon.flags & 0x80000000) == 0);
|
|
polygon.flags &= ~0x80000000;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::validate(Geometry *geometry, GeometryI **geometryi)
|
|
{
|
|
if (!geometryi)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (!geometry)
|
|
{
|
|
return FMOD_ERR_INVALID_HANDLE;
|
|
}
|
|
|
|
*geometryi = (GeometryI *)geometry;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT GeometryI::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details)
|
|
{
|
|
#ifdef FMOD_SUPPORT_MEMORYTRACKER
|
|
GETMEMORYINFO_IMPL
|
|
#else
|
|
return FMOD_ERR_UNIMPLEMENTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
|
|
#ifdef FMOD_SUPPORT_MEMORYTRACKER
|
|
|
|
FMOD_RESULT GeometryI::getMemoryUsedImpl(MemoryTracker *tracker)
|
|
{
|
|
//AJS incomplete
|
|
tracker->add(false, FMOD_MEMBITS_GEOMETRY, sizeof(*this));
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
#endif // FMOD_SUPPORT_GEOMETRY
|