2016-05-14 05:00:02 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// 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.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2016-05-29 05:58:02 +00:00
|
|
|
#include "T3D/components/collision/collisionComponent.h"
|
2019-02-24 07:50:38 +00:00
|
|
|
#include "scene/sceneObject.h"
|
|
|
|
|
#include "T3D/entity.h"
|
2016-05-14 05:00:02 +00:00
|
|
|
#include "console/engineAPI.h"
|
|
|
|
|
#include "T3D/trigger.h"
|
2019-02-24 07:50:38 +00:00
|
|
|
#include "materials/baseMatInstance.h"
|
|
|
|
|
#include "collision/extrudedPolyList.h"
|
2016-05-14 05:00:02 +00:00
|
|
|
#include "opcode/Opcode.h"
|
|
|
|
|
#include "opcode/Ice/IceAABB.h"
|
|
|
|
|
#include "opcode/Ice/IcePoint.h"
|
|
|
|
|
#include "opcode/OPC_AABBTree.h"
|
|
|
|
|
#include "opcode/OPC_AABBCollider.h"
|
2019-02-24 07:50:38 +00:00
|
|
|
#include "collision/clippedPolyList.h"
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
static F32 sTractionDistance = 0.04f;
|
|
|
|
|
|
|
|
|
|
IMPLEMENT_CONOBJECT(CollisionComponent);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
|
|
|
|
CollisionComponent::CollisionComponent() : Component()
|
|
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
mFriendlyName = "Collision Component";
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
mComponentType = "Collision";
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
mDescription = getDescriptionText("A stub component class that collision components should inherit from.");
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
mBlockColliding = true;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
|
|
|
|
CollisionMoveMask = (TerrainObjectType | PlayerObjectType |
|
|
|
|
|
StaticShapeObjectType | VehicleObjectType |
|
|
|
|
|
VehicleBlockerObjectType | DynamicShapeObjectType | StaticObjectType | EntityObjectType | TriggerObjectType);
|
|
|
|
|
|
2018-02-04 22:21:07 +00:00
|
|
|
mPhysicsRep = nullptr;
|
|
|
|
|
mPhysicsWorld = nullptr;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2018-02-04 22:21:07 +00:00
|
|
|
mTimeoutList = nullptr;
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CollisionComponent::~CollisionComponent()
|
|
|
|
|
{
|
|
|
|
|
for (S32 i = 0; i < mFields.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
ComponentField &field = mFields[i];
|
|
|
|
|
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SAFE_DELETE_ARRAY(mDescription);
|
2019-02-24 07:50:38 +00:00
|
|
|
|
|
|
|
|
SAFE_DELETE(mPhysicsRep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CollisionComponent::checkCollisions(const F32 travelTime, Point3F *velocity, Point3F start)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CollisionComponent::updateCollisions(F32 time, VectorF vector, VectorF velocity)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
void CollisionComponent::updateWorkingCollisionSet(const U32 mask)
|
|
|
|
|
{
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
void CollisionComponent::handleCollisionList( CollisionList &collisionList, VectorF velocity )
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
Collision bestCol;
|
|
|
|
|
|
|
|
|
|
mCollisionList = collisionList;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
for (U32 i=0; i < collisionList.getCount(); ++i)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
Collision& colCheck = collisionList[i];
|
|
|
|
|
|
|
|
|
|
if (colCheck.object)
|
|
|
|
|
{
|
|
|
|
|
if (colCheck.object->getTypeMask() & PlayerObjectType)
|
|
|
|
|
{
|
|
|
|
|
handleCollision( colCheck, velocity );
|
|
|
|
|
}
|
|
|
|
|
else if (colCheck.object->getTypeMask() & TriggerObjectType)
|
|
|
|
|
{
|
|
|
|
|
// We've hit it's bounding box, that's close enough for triggers
|
|
|
|
|
Trigger* pTrigger = static_cast<Trigger*>(colCheck.object);
|
|
|
|
|
|
|
|
|
|
Component *comp = dynamic_cast<Component*>(this);
|
|
|
|
|
pTrigger->potentialEnterObject(comp->getOwner());
|
|
|
|
|
}
|
|
|
|
|
else if (colCheck.object->getTypeMask() & DynamicShapeObjectType)
|
|
|
|
|
{
|
|
|
|
|
Con::printf("HIT A GENERICALLY DYNAMIC OBJECT");
|
|
|
|
|
handleCollision(colCheck, velocity);
|
|
|
|
|
}
|
|
|
|
|
else if(colCheck.object->getTypeMask() & EntityObjectType)
|
|
|
|
|
{
|
|
|
|
|
Entity* ent = dynamic_cast<Entity*>(colCheck.object);
|
|
|
|
|
if (ent)
|
|
|
|
|
{
|
|
|
|
|
CollisionComponent *colObjectInterface = ent->getComponent<CollisionComponent>();
|
|
|
|
|
if (colObjectInterface)
|
|
|
|
|
{
|
|
|
|
|
//convert us to our component
|
|
|
|
|
Component *thisComp = dynamic_cast<Component*>(this);
|
|
|
|
|
if (thisComp)
|
|
|
|
|
{
|
|
|
|
|
colObjectInterface->onCollisionSignal.trigger(thisComp->getOwner());
|
|
|
|
|
|
|
|
|
|
//TODO: properly do this
|
|
|
|
|
Collision oppositeCol = colCheck;
|
|
|
|
|
oppositeCol.object = thisComp->getOwner();
|
|
|
|
|
|
|
|
|
|
colObjectInterface->handleCollision(oppositeCol, velocity);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
handleCollision(colCheck, velocity);
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
2019-02-24 07:50:38 +00:00
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
void CollisionComponent::handleCollision( Collision &col, VectorF velocity )
|
|
|
|
|
{
|
|
|
|
|
if (col.object && (mContactInfo.contactObject == NULL ||
|
|
|
|
|
col.object->getId() != mContactInfo.contactObject->getId()))
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
queueCollision(col.object, velocity - col.object->getVelocity());
|
|
|
|
|
|
|
|
|
|
//do the callbacks to script for this collision
|
|
|
|
|
Component *comp = dynamic_cast<Component*>(this);
|
|
|
|
|
if (comp->isMethod("onCollision"))
|
|
|
|
|
{
|
|
|
|
|
S32 matId = col.material != NULL ? col.material->getMaterial()->getId() : 0;
|
|
|
|
|
Con::executef(comp, "onCollision", col.object, col.normal, col.point, matId, velocity);
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (comp->getOwner()->isMethod("onCollisionEvent"))
|
|
|
|
|
{
|
|
|
|
|
S32 matId = col.material != NULL ? col.material->getMaterial()->getId() : 0;
|
|
|
|
|
Con::executef(comp->getOwner(), "onCollisionEvent", col.object, col.normal, col.point, matId, velocity);
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
void CollisionComponent::handleCollisionNotifyList()
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
//special handling for any collision components we should notify that a collision happened.
|
|
|
|
|
for (U32 i = 0; i < mCollisionNotifyList.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
//convert us to our component
|
|
|
|
|
Component *thisComp = dynamic_cast<Component*>(this);
|
|
|
|
|
if (thisComp)
|
|
|
|
|
{
|
|
|
|
|
mCollisionNotifyList[i]->onCollisionSignal.trigger(thisComp->getOwner());
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
mCollisionNotifyList.clear();
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
Chunker<CollisionComponent::CollisionTimeout> sCollisionTimeoutChunker;
|
|
|
|
|
CollisionComponent::CollisionTimeout* CollisionComponent::sFreeTimeoutList = 0;
|
|
|
|
|
|
|
|
|
|
void CollisionComponent::queueCollision( SceneObject *obj, const VectorF &vec)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
// Add object to list of collisions.
|
|
|
|
|
SimTime time = Sim::getCurrentTime();
|
|
|
|
|
S32 num = obj->getId();
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
CollisionTimeout** adr = &mTimeoutList;
|
|
|
|
|
CollisionTimeout* ptr = mTimeoutList;
|
|
|
|
|
while (ptr)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
if (ptr->objectNumber == num)
|
|
|
|
|
{
|
|
|
|
|
if (ptr->expireTime < time)
|
|
|
|
|
{
|
|
|
|
|
ptr->expireTime = time + CollisionTimeoutValue;
|
|
|
|
|
ptr->object = obj;
|
|
|
|
|
ptr->vector = vec;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Recover expired entries
|
|
|
|
|
if (ptr->expireTime < time)
|
|
|
|
|
{
|
|
|
|
|
CollisionTimeout* cur = ptr;
|
|
|
|
|
*adr = ptr->next;
|
|
|
|
|
ptr = ptr->next;
|
|
|
|
|
cur->next = sFreeTimeoutList;
|
|
|
|
|
sFreeTimeoutList = cur;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
adr = &ptr->next;
|
|
|
|
|
ptr = ptr->next;
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// New entry for the object
|
|
|
|
|
if (sFreeTimeoutList != NULL)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
ptr = sFreeTimeoutList;
|
|
|
|
|
sFreeTimeoutList = ptr->next;
|
|
|
|
|
ptr->next = NULL;
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
2019-02-24 07:50:38 +00:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ptr = sCollisionTimeoutChunker.alloc();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ptr->object = obj;
|
|
|
|
|
ptr->objectNumber = obj->getId();
|
|
|
|
|
ptr->vector = vec;
|
|
|
|
|
ptr->expireTime = time + CollisionTimeoutValue;
|
|
|
|
|
ptr->next = mTimeoutList;
|
|
|
|
|
|
|
|
|
|
mTimeoutList = ptr;
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
bool CollisionComponent::checkEarlyOut(Point3F start, VectorF velocity, F32 time, Box3F objectBox, Point3F objectScale,
|
|
|
|
|
Box3F collisionBox, U32 collisionMask, CollisionWorkingList &colWorkingList)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
Point3F end = start + velocity * time;
|
|
|
|
|
Point3F distance = end - start;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
Box3F scaledBox = objectBox;
|
|
|
|
|
scaledBox.minExtents.convolve(objectScale);
|
|
|
|
|
scaledBox.maxExtents.convolve(objectScale);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (mFabs(distance.x) < objectBox.len_x() &&
|
|
|
|
|
mFabs(distance.y) < objectBox.len_y() &&
|
|
|
|
|
mFabs(distance.z) < objectBox.len_z())
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
// We can potentially early out of this. If there are no polys in the clipped polylist at our
|
|
|
|
|
// end position, then we can bail, and just set start = end;
|
|
|
|
|
Box3F wBox = scaledBox;
|
|
|
|
|
wBox.minExtents += end;
|
|
|
|
|
wBox.maxExtents += end;
|
|
|
|
|
|
|
|
|
|
static EarlyOutPolyList eaPolyList;
|
|
|
|
|
eaPolyList.clear();
|
|
|
|
|
eaPolyList.mNormal.set(0.0f, 0.0f, 0.0f);
|
|
|
|
|
eaPolyList.mPlaneList.clear();
|
|
|
|
|
eaPolyList.mPlaneList.setSize(6);
|
|
|
|
|
eaPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1.0f, 0.0f, 0.0f));
|
|
|
|
|
eaPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0.0f, 1.0f, 0.0f));
|
|
|
|
|
eaPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1.0f, 0.0f, 0.0f));
|
|
|
|
|
eaPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0.0f, -1.0f, 0.0f));
|
|
|
|
|
eaPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0.0f, 0.0f, -1.0f));
|
|
|
|
|
eaPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0.0f, 0.0f, 1.0f));
|
|
|
|
|
|
|
|
|
|
// Build list from convex states here...
|
|
|
|
|
CollisionWorkingList& rList = colWorkingList;
|
|
|
|
|
CollisionWorkingList* pList = rList.wLink.mNext;
|
|
|
|
|
while (pList != &rList)
|
|
|
|
|
{
|
|
|
|
|
Convex* pConvex = pList->mConvex;
|
|
|
|
|
|
|
|
|
|
if (pConvex->getObject()->getTypeMask() & collisionMask)
|
|
|
|
|
{
|
|
|
|
|
Box3F convexBox = pConvex->getBoundingBox();
|
|
|
|
|
|
|
|
|
|
if (wBox.isOverlapped(convexBox))
|
|
|
|
|
{
|
|
|
|
|
// No need to separate out the physical zones here, we want those
|
|
|
|
|
// to cause a fallthrough as well...
|
|
|
|
|
pConvex->getPolyList(&eaPolyList);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pList = pList->wLink.mNext;
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (eaPolyList.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
return false;
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
Collision* CollisionComponent::getCollision(S32 col)
|
|
|
|
|
{
|
|
|
|
|
if(col < mCollisionList.getCount() && col >= 0)
|
|
|
|
|
return &mCollisionList[col];
|
|
|
|
|
else
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Point3F CollisionComponent::getContactNormal()
|
|
|
|
|
{
|
|
|
|
|
return mContactInfo.contactNormal;
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
bool CollisionComponent::hasContact()
|
|
|
|
|
{
|
|
|
|
|
if (mContactInfo.contactObject)
|
|
|
|
|
return true;
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
S32 CollisionComponent::getCollisionCount()
|
|
|
|
|
{
|
|
|
|
|
return mCollisionList.getCount();
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
Point3F CollisionComponent::getCollisionNormal(S32 collisionIndex)
|
|
|
|
|
{
|
|
|
|
|
if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
|
|
|
|
|
return Point3F::Zero;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
return mCollisionList[collisionIndex].normal;
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
F32 CollisionComponent::getCollisionAngle(S32 collisionIndex, Point3F upVector)
|
|
|
|
|
{
|
|
|
|
|
if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
|
|
|
|
|
return 0.0f;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
return mRadToDeg(mAcos(mDot(mCollisionList[collisionIndex].normal, upVector)));
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
S32 CollisionComponent::getBestCollision(Point3F upVector)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
S32 bestCollision = -1;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
F32 bestAngle = 360.f;
|
|
|
|
|
S32 count = mCollisionList.getCount();
|
|
|
|
|
for (U32 i = 0; i < count; ++i)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
F32 angle = mRadToDeg(mAcos(mDot(mCollisionList[i].normal, upVector)));
|
|
|
|
|
|
|
|
|
|
if (angle < bestAngle)
|
|
|
|
|
{
|
|
|
|
|
bestCollision = i;
|
|
|
|
|
bestAngle = angle;
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
2019-02-24 07:50:38 +00:00
|
|
|
|
|
|
|
|
return bestCollision;
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
F32 CollisionComponent::getBestCollisionAngle(VectorF upVector)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
S32 bestCol = getBestCollision(upVector);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (bestCol == -1)
|
|
|
|
|
return 0;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
return getCollisionAngle(bestCol, upVector);
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
//
|
|
|
|
|
bool CollisionComponent::buildConvexOpcode(TSShapeInstance* sI, S32 dl, const Box3F &bounds, Convex *c, Convex *list)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
AssertFatal(dl >= 0 && dl < sI->getShape()->details.size(), "TSShapeInstance::buildConvexOpcode");
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
TSShape* shape = sI->getShape();
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
const MatrixF &objMat = mOwner->getObjToWorld();
|
|
|
|
|
const Point3F &objScale = mOwner->getScale();
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// get subshape and object detail
|
|
|
|
|
const TSDetail * detail = &shape->details[dl];
|
|
|
|
|
S32 ss = detail->subShapeNum;
|
|
|
|
|
S32 od = detail->objectDetailNum;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// nothing emitted yet...
|
|
|
|
|
bool emitted = false;
|
|
|
|
|
|
|
|
|
|
S32 start = shape->subShapeFirstObject[ss];
|
|
|
|
|
S32 end = shape->subShapeNumObjects[ss] + start;
|
|
|
|
|
if (start<end)
|
|
|
|
|
{
|
|
|
|
|
MatrixF initialMat = objMat;
|
|
|
|
|
Point3F initialScale = objScale;
|
|
|
|
|
|
|
|
|
|
// set up for first object's node
|
|
|
|
|
MatrixF mat;
|
|
|
|
|
MatrixF scaleMat(true);
|
|
|
|
|
F32* p = scaleMat;
|
|
|
|
|
p[0] = initialScale.x;
|
|
|
|
|
p[5] = initialScale.y;
|
|
|
|
|
p[10] = initialScale.z;
|
|
|
|
|
const MatrixF * previousMat = &sI->mMeshObjects[start].getTransform();
|
|
|
|
|
mat.mul(initialMat, scaleMat);
|
|
|
|
|
mat.mul(*previousMat);
|
|
|
|
|
|
|
|
|
|
// Update our bounding box...
|
|
|
|
|
Box3F localBox = bounds;
|
|
|
|
|
MatrixF otherMat = mat;
|
|
|
|
|
otherMat.inverse();
|
|
|
|
|
otherMat.mul(localBox);
|
|
|
|
|
|
|
|
|
|
// run through objects and collide
|
|
|
|
|
for (S32 i = start; i<end; i++)
|
|
|
|
|
{
|
|
|
|
|
TSShapeInstance::MeshObjectInstance * meshInstance = &sI->mMeshObjects[i];
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (od >= meshInstance->object->numMeshes)
|
|
|
|
|
continue;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (&meshInstance->getTransform() != previousMat)
|
|
|
|
|
{
|
|
|
|
|
// different node from before, set up for this node
|
|
|
|
|
previousMat = &meshInstance->getTransform();
|
|
|
|
|
|
|
|
|
|
if (previousMat != NULL)
|
|
|
|
|
{
|
|
|
|
|
mat.mul(initialMat, scaleMat);
|
|
|
|
|
mat.mul(*previousMat);
|
|
|
|
|
|
|
|
|
|
// Update our bounding box...
|
|
|
|
|
otherMat = mat;
|
|
|
|
|
otherMat.inverse();
|
|
|
|
|
localBox = bounds;
|
|
|
|
|
otherMat.mul(localBox);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// collide... note we pass the original mech transform
|
|
|
|
|
// here so that the convex data returned is in mesh space.
|
|
|
|
|
TSMesh * mesh = meshInstance->getMesh(od);
|
|
|
|
|
if (mesh && !meshInstance->forceHidden && meshInstance->visible > 0.01f && localBox.isOverlapped(mesh->getBounds()))
|
|
|
|
|
emitted |= buildMeshOpcode(mesh, *previousMat, localBox, c, list);
|
|
|
|
|
else
|
|
|
|
|
emitted |= false;
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
return emitted;
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
bool CollisionComponent::buildMeshOpcode(TSMesh *mesh, const MatrixF &meshToObjectMat,
|
|
|
|
|
const Box3F &nodeBox, Convex *convex, Convex *list)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
/*PROFILE_SCOPE(MeshCollider_buildConvexOpcode);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// This is small... there is no win for preallocating it.
|
|
|
|
|
Opcode::AABBCollider opCollider;
|
|
|
|
|
opCollider.SetPrimitiveTests(true);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// This isn't really needed within the AABBCollider as
|
|
|
|
|
// we don't use temporal coherance... use a static to
|
|
|
|
|
// remove the allocation overhead.
|
|
|
|
|
static Opcode::AABBCache opCache;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
IceMaths::AABB opBox;
|
|
|
|
|
opBox.SetMinMax(Point(nodeBox.minExtents.x, nodeBox.minExtents.y, nodeBox.minExtents.z),
|
|
|
|
|
Point(nodeBox.maxExtents.x, nodeBox.maxExtents.y, nodeBox.maxExtents.z));
|
|
|
|
|
Opcode::CollisionAABB opCBox(opBox);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (!opCollider.Collide(opCache, opCBox, *mesh->mOptTree))
|
|
|
|
|
return false;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
U32 cnt = opCollider.GetNbTouchedPrimitives();
|
|
|
|
|
const udword *idx = opCollider.GetTouchedPrimitives();
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
Opcode::VertexPointers vp;
|
|
|
|
|
for (S32 i = 0; i < cnt; i++)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
// First, check our active convexes for a potential match (and clean things
|
|
|
|
|
// up, too.)
|
|
|
|
|
const U32 curIdx = idx[i];
|
|
|
|
|
|
|
|
|
|
// See if the square already exists as part of the working set.
|
|
|
|
|
bool gotMatch = false;
|
|
|
|
|
CollisionWorkingList& wl = convex->getWorkingList();
|
|
|
|
|
for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext)
|
|
|
|
|
{
|
|
|
|
|
if (itr->mConvex->getType() != TSPolysoupConvexType)
|
|
|
|
|
continue;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
const MeshColliderPolysoupConvex *chunkc = static_cast<MeshColliderPolysoupConvex*>(itr->mConvex);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (chunkc->getObject() != mOwner)
|
|
|
|
|
continue;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (chunkc->mesh != mesh)
|
|
|
|
|
continue;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (chunkc->idx != curIdx)
|
|
|
|
|
continue;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// A match! Don't need to add it.
|
|
|
|
|
gotMatch = true;
|
|
|
|
|
break;
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (gotMatch)
|
|
|
|
|
continue;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// Get the triangle...
|
|
|
|
|
mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, idx[i]);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
Point3F a(vp.Vertex[0]->x, vp.Vertex[0]->y, vp.Vertex[0]->z);
|
|
|
|
|
Point3F b(vp.Vertex[1]->x, vp.Vertex[1]->y, vp.Vertex[1]->z);
|
|
|
|
|
Point3F c(vp.Vertex[2]->x, vp.Vertex[2]->y, vp.Vertex[2]->z);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// Transform the result into object space!
|
|
|
|
|
meshToObjectMat.mulP(a);
|
|
|
|
|
meshToObjectMat.mulP(b);
|
|
|
|
|
meshToObjectMat.mulP(c);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
//If we're not doing debug rendering on the client, then set up our convex list as normal
|
|
|
|
|
PlaneF p(c, b, a);
|
|
|
|
|
Point3F peak = ((a + b + c) / 3.0f) - (p * 0.15f);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// Set up the convex...
|
|
|
|
|
MeshColliderPolysoupConvex *cp = new MeshColliderPolysoupConvex();
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
list->registerObject(cp);
|
|
|
|
|
convex->addToWorkingList(cp);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
cp->mesh = mesh;
|
|
|
|
|
cp->idx = curIdx;
|
|
|
|
|
cp->mObject = mOwner;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
cp->normal = p;
|
|
|
|
|
cp->verts[0] = a;
|
|
|
|
|
cp->verts[1] = b;
|
|
|
|
|
cp->verts[2] = c;
|
|
|
|
|
cp->verts[3] = peak;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// Update the bounding box.
|
|
|
|
|
Box3F &bounds = cp->box;
|
|
|
|
|
bounds.minExtents.set(F32_MAX, F32_MAX, F32_MAX);
|
|
|
|
|
bounds.maxExtents.set(-F32_MAX, -F32_MAX, -F32_MAX);
|
|
|
|
|
|
|
|
|
|
bounds.minExtents.setMin(a);
|
|
|
|
|
bounds.minExtents.setMin(b);
|
|
|
|
|
bounds.minExtents.setMin(c);
|
|
|
|
|
bounds.minExtents.setMin(peak);
|
|
|
|
|
|
|
|
|
|
bounds.maxExtents.setMax(a);
|
|
|
|
|
bounds.maxExtents.setMax(b);
|
|
|
|
|
bounds.maxExtents.setMax(c);
|
|
|
|
|
bounds.maxExtents.setMax(peak);
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
return true;*/
|
|
|
|
|
return false;
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
bool CollisionComponent::castRayOpcode(S32 dl, const Point3F & startPos, const Point3F & endPos, RayInfo *info)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
// if dl==-1, nothing to do
|
|
|
|
|
//if (dl == -1 || !getShapeInstance())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/*TSShape *shape = getShapeInstance()->getShape();
|
|
|
|
|
|
|
|
|
|
AssertFatal(dl >= 0 && dl < shape->details.size(), "TSShapeInstance::castRayOpcode");
|
|
|
|
|
|
|
|
|
|
info->t = 100.f;
|
|
|
|
|
|
|
|
|
|
// get subshape and object detail
|
|
|
|
|
const TSDetail * detail = &shape->details[dl];
|
|
|
|
|
S32 ss = detail->subShapeNum;
|
|
|
|
|
if (ss < 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
S32 od = detail->objectDetailNum;
|
|
|
|
|
|
|
|
|
|
// nothing emitted yet...
|
|
|
|
|
bool emitted = false;
|
|
|
|
|
|
|
|
|
|
const MatrixF* saveMat = NULL;
|
|
|
|
|
S32 start = shape->subShapeFirstObject[ss];
|
|
|
|
|
S32 end = shape->subShapeNumObjects[ss] + start;
|
|
|
|
|
if (start<end)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
MatrixF mat;
|
|
|
|
|
const MatrixF * previousMat = &getShapeInstance()->mMeshObjects[start].getTransform();
|
|
|
|
|
mat = *previousMat;
|
|
|
|
|
mat.inverse();
|
|
|
|
|
Point3F localStart, localEnd;
|
|
|
|
|
mat.mulP(startPos, &localStart);
|
|
|
|
|
mat.mulP(endPos, &localEnd);
|
|
|
|
|
|
|
|
|
|
// run through objects and collide
|
|
|
|
|
for (S32 i = start; i<end; i++)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
TSShapeInstance::MeshObjectInstance * meshInstance = &getShapeInstance()->mMeshObjects[i];
|
|
|
|
|
|
|
|
|
|
if (od >= meshInstance->object->numMeshes)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (&meshInstance->getTransform() != previousMat)
|
|
|
|
|
{
|
|
|
|
|
// different node from before, set up for this node
|
|
|
|
|
previousMat = &meshInstance->getTransform();
|
|
|
|
|
|
|
|
|
|
if (previousMat != NULL)
|
|
|
|
|
{
|
|
|
|
|
mat = *previousMat;
|
|
|
|
|
mat.inverse();
|
|
|
|
|
mat.mulP(startPos, &localStart);
|
|
|
|
|
mat.mulP(endPos, &localEnd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// collide...
|
|
|
|
|
TSMesh * mesh = meshInstance->getMesh(od);
|
|
|
|
|
if (mesh && !meshInstance->forceHidden && meshInstance->visible > 0.01f)
|
|
|
|
|
{
|
|
|
|
|
if (castRayMeshOpcode(mesh, localStart, localEnd, info, getShapeInstance()->mMaterialList))
|
|
|
|
|
{
|
|
|
|
|
saveMat = previousMat;
|
|
|
|
|
emitted = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (emitted)
|
|
|
|
|
{
|
|
|
|
|
saveMat->mulV(info->normal);
|
|
|
|
|
info->point = endPos - startPos;
|
|
|
|
|
info->point *= info->t;
|
|
|
|
|
info->point += startPos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return emitted;*/
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
static Point3F texGenAxis[18] =
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
Point3F(0,0,1), Point3F(1,0,0), Point3F(0,-1,0),
|
|
|
|
|
Point3F(0,0,-1), Point3F(1,0,0), Point3F(0,1,0),
|
|
|
|
|
Point3F(1,0,0), Point3F(0,1,0), Point3F(0,0,1),
|
|
|
|
|
Point3F(-1,0,0), Point3F(0,1,0), Point3F(0,0,-1),
|
|
|
|
|
Point3F(0,1,0), Point3F(1,0,0), Point3F(0,0,1),
|
|
|
|
|
Point3F(0,-1,0), Point3F(-1,0,0), Point3F(0,0,-1)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool CollisionComponent::castRayMeshOpcode(TSMesh *mesh, const Point3F &s, const Point3F &e, RayInfo *info, TSMaterialList *materials)
|
|
|
|
|
{
|
|
|
|
|
Opcode::RayCollider ray;
|
|
|
|
|
Opcode::CollisionFaces cfs;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
IceMaths::Point dir(e.x - s.x, e.y - s.y, e.z - s.z);
|
|
|
|
|
const F32 rayLen = dir.Magnitude();
|
|
|
|
|
IceMaths::Ray vec(Point(s.x, s.y, s.z), dir.Normalize());
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
ray.SetDestination(&cfs);
|
|
|
|
|
ray.SetFirstContact(false);
|
|
|
|
|
ray.SetClosestHit(true);
|
|
|
|
|
ray.SetPrimitiveTests(true);
|
|
|
|
|
ray.SetCulling(true);
|
|
|
|
|
ray.SetMaxDist(rayLen);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
AssertFatal(ray.ValidateSettings() == NULL, "invalid ray settings");
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// Do collision.
|
|
|
|
|
bool safety = ray.Collide(vec, *mesh->mOptTree);
|
|
|
|
|
AssertFatal(safety, "CollisionComponent::castRayOpcode - no good ray collide!");
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// If no hit, just skip out.
|
|
|
|
|
if (cfs.GetNbFaces() == 0)
|
|
|
|
|
return false;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// Got a hit!
|
|
|
|
|
AssertFatal(cfs.GetNbFaces() == 1, "bad");
|
|
|
|
|
const Opcode::CollisionFace &face = cfs.GetFaces()[0];
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// If the cast was successful let's check if the t value is less than what we had
|
|
|
|
|
// and toggle the collision boolean
|
|
|
|
|
// Stupid t... i prefer coffee
|
|
|
|
|
const F32 t = face.mDistance / rayLen;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (t < 0.0f || t > 1.0f)
|
|
|
|
|
return false;
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (t <= info->t)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
info->t = t;
|
|
|
|
|
|
|
|
|
|
// Calculate the normal.
|
|
|
|
|
Opcode::VertexPointers vp;
|
|
|
|
|
mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, face.mFaceID);
|
|
|
|
|
|
|
|
|
|
if (materials && vp.MatIdx >= 0 && vp.MatIdx < materials->size())
|
|
|
|
|
info->material = materials->getMaterialInst(vp.MatIdx);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
// Get the two edges.
|
|
|
|
|
IceMaths::Point baseVert = *vp.Vertex[0];
|
|
|
|
|
IceMaths::Point a = *vp.Vertex[1] - baseVert;
|
|
|
|
|
IceMaths::Point b = *vp.Vertex[2] - baseVert;
|
|
|
|
|
|
|
|
|
|
IceMaths::Point n;
|
|
|
|
|
n.Cross(a, b);
|
|
|
|
|
n.Normalize();
|
|
|
|
|
|
|
|
|
|
info->normal.set(n.x, n.y, n.z);
|
|
|
|
|
|
|
|
|
|
// generate UV coordinate across mesh based on
|
|
|
|
|
// matching normals, this isn't done by default and is
|
|
|
|
|
// primarily of interest in matching a collision point to
|
|
|
|
|
// either a GUI control coordinate or finding a hit pixel in texture space
|
|
|
|
|
if (info->generateTexCoord)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
baseVert = *vp.Vertex[0];
|
|
|
|
|
a = *vp.Vertex[1];
|
|
|
|
|
b = *vp.Vertex[2];
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
Point3F facePoint = (1.0f - face.mU - face.mV) * Point3F(baseVert.x, baseVert.y, baseVert.z)
|
|
|
|
|
+ face.mU * Point3F(a.x, a.y, a.z) + face.mV * Point3F(b.x, b.y, b.z);
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
U32 faces[1024];
|
|
|
|
|
U32 numFaces = 0;
|
|
|
|
|
for (U32 i = 0; i < mesh->mOptTree->GetMeshInterface()->GetNbTriangles(); i++)
|
|
|
|
|
{
|
|
|
|
|
if (i == face.mFaceID)
|
|
|
|
|
{
|
|
|
|
|
faces[numFaces++] = i;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
IceMaths::Point n2;
|
|
|
|
|
|
|
|
|
|
mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, i);
|
|
|
|
|
|
|
|
|
|
baseVert = *vp.Vertex[0];
|
|
|
|
|
a = *vp.Vertex[1] - baseVert;
|
|
|
|
|
b = *vp.Vertex[2] - baseVert;
|
|
|
|
|
n2.Cross(a, b);
|
|
|
|
|
n2.Normalize();
|
|
|
|
|
|
|
|
|
|
F32 eps = .01f;
|
|
|
|
|
if (mFabs(n.x - n2.x) < eps && mFabs(n.y - n2.y) < eps && mFabs(n.z - n2.z) < eps)
|
|
|
|
|
{
|
|
|
|
|
faces[numFaces++] = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (numFaces == 1024)
|
|
|
|
|
{
|
|
|
|
|
// too many faces in this collision mesh for UV generation
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Point3F min(F32_MAX, F32_MAX, F32_MAX);
|
|
|
|
|
Point3F max(-F32_MAX, -F32_MAX, -F32_MAX);
|
|
|
|
|
|
|
|
|
|
for (U32 i = 0; i < numFaces; i++)
|
|
|
|
|
{
|
|
|
|
|
mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, faces[i]);
|
|
|
|
|
|
|
|
|
|
for (U32 j = 0; j < 3; j++)
|
|
|
|
|
{
|
|
|
|
|
a = *vp.Vertex[j];
|
|
|
|
|
|
|
|
|
|
if (a.x < min.x)
|
|
|
|
|
min.x = a.x;
|
|
|
|
|
if (a.y < min.y)
|
|
|
|
|
min.y = a.y;
|
|
|
|
|
if (a.z < min.z)
|
|
|
|
|
min.z = a.z;
|
|
|
|
|
|
|
|
|
|
if (a.x > max.x)
|
|
|
|
|
max.x = a.x;
|
|
|
|
|
if (a.y > max.y)
|
|
|
|
|
max.y = a.y;
|
|
|
|
|
if (a.z > max.z)
|
|
|
|
|
max.z = a.z;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// slerp
|
|
|
|
|
Point3F s = ((max - min) - (facePoint - min)) / (max - min);
|
|
|
|
|
|
|
|
|
|
// compute axis
|
|
|
|
|
S32 bestAxis = 0;
|
|
|
|
|
F32 best = 0.f;
|
|
|
|
|
|
|
|
|
|
for (U32 i = 0; i < 6; i++)
|
|
|
|
|
{
|
|
|
|
|
F32 dot = mDot(info->normal, texGenAxis[i * 3]);
|
|
|
|
|
if (dot > best)
|
|
|
|
|
{
|
|
|
|
|
best = dot;
|
|
|
|
|
bestAxis = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Point3F xv = texGenAxis[bestAxis * 3 + 1];
|
|
|
|
|
Point3F yv = texGenAxis[bestAxis * 3 + 2];
|
|
|
|
|
|
|
|
|
|
S32 sv, tv;
|
|
|
|
|
|
|
|
|
|
if (xv.x)
|
|
|
|
|
sv = 0;
|
|
|
|
|
else if (xv.y)
|
|
|
|
|
sv = 1;
|
|
|
|
|
else
|
|
|
|
|
sv = 2;
|
|
|
|
|
|
|
|
|
|
if (yv.x)
|
|
|
|
|
tv = 0;
|
|
|
|
|
else if (yv.y)
|
|
|
|
|
tv = 1;
|
|
|
|
|
else
|
|
|
|
|
tv = 2;
|
|
|
|
|
|
|
|
|
|
// handle coord translation
|
|
|
|
|
if (bestAxis == 2 || bestAxis == 3)
|
|
|
|
|
{
|
|
|
|
|
S32 x = sv;
|
|
|
|
|
sv = tv;
|
|
|
|
|
tv = x;
|
|
|
|
|
|
|
|
|
|
if (yv.z < 0)
|
|
|
|
|
s[sv] = 1.f - s[sv];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bestAxis < 2)
|
|
|
|
|
{
|
|
|
|
|
if (yv.y < 0)
|
|
|
|
|
s[sv] = 1.f - s[sv];
|
|
|
|
|
}
|
2016-05-14 05:00:02 +00:00
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
if (bestAxis > 3)
|
2016-05-14 05:00:02 +00:00
|
|
|
{
|
2019-02-24 07:50:38 +00:00
|
|
|
s[sv] = 1.f - s[sv];
|
|
|
|
|
if (yv.z > 0)
|
|
|
|
|
s[tv] = 1.f - s[tv];
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// done!
|
|
|
|
|
info->texCoord.set(s[sv], s[tv]);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 07:50:38 +00:00
|
|
|
return false;
|
2016-05-14 05:00:02 +00:00
|
|
|
}
|