📄 sceneobject.cc
字号:
// - The optimal grid size isn't necessarily what we have set here. possibly
// a resolution of 16 meters would give better results
// - The line rasterizer is pretty lame. Unfortunately we can't use a
// simple bres. here, since we need to check every grid element that the line
// passes through, which bres does _not_ do for us. Possibly there's a
// rasterizer for anti-aliased lines that will serve better than what
// we have below.
//
bool Container::castRay(const Point3F &start, const Point3F &end, U32 mask, RayInfo* info)
{
PROFILE_START(ContainerCastRay);
F32 currentT = 2.0;
smCurrSeqKey++;
SceneObjectRef* chain = mOverflowBin.nextInBin;
while (chain)
{
SceneObject* ptr = chain->object;
if (ptr->getContainerSeqKey() != smCurrSeqKey)
{
ptr->setContainerSeqKey(smCurrSeqKey);
// In the overflow bin, the world box is always going to intersect the line,
// so we can omit that test...
if ((ptr->getType() & mask) != 0 &&
ptr->isCollisionEnabled() == true)
{
Point3F xformedStart, xformedEnd;
ptr->mWorldToObj.mulP(start, &xformedStart);
ptr->mWorldToObj.mulP(end, &xformedEnd);
xformedStart.convolveInverse(ptr->mObjScale);
xformedEnd.convolveInverse(ptr->mObjScale);
RayInfo ri;
if (ptr->castRay(xformedStart, xformedEnd, &ri))
{
if(ri.t < currentT)
{
*info = ri;
info->point.interpolate(start, end, info->t);
currentT = ri.t;
}
}
}
}
chain = chain->nextInBin;
}
// These are just for rasterizing the line against the grid. We want the x coord
// of the start to be <= the x coord of the end
Point3F normalStart, normalEnd;
if (start.x <= end.x)
{
normalStart = start;
normalEnd = end;
}
else
{
normalStart = end;
normalEnd = start;
}
// Ok, let's scan the grids. The simplest way to do this will be to scan across in
// x, finding the y range for each affected bin...
U32 minX, maxX;
U32 minY, maxY;
//if (normalStart.x == normalEnd.x)
// Con::printf("X start = %g, end = %g", normalStart.x, normalEnd.x);
getBinRange(normalStart.x, normalEnd.x, minX, maxX);
getBinRange(getMin(normalStart.y, normalEnd.y),
getMax(normalStart.y, normalEnd.y), minY, maxY);
//if (normalStart.x == normalEnd.x && minX != maxX)
// Con::printf("X min = %d, max = %d", minX, maxX);
//if (normalStart.y == normalEnd.y && minY != maxY)
// Con::printf("Y min = %d, max = %d", minY, maxY);
// We'll optimize the case that the line is contained in one bin row or column, which
// will be quite a few lines. No sense doing more work than we have to...
//
if ((mFabs(normalStart.x - normalEnd.x) < csmTotalBinSize && minX == maxX) ||
(mFabs(normalStart.y - normalEnd.y) < csmTotalBinSize && minY == maxY))
{
U32 count;
U32 incX, incY;
if (minX == maxX)
{
count = maxY - minY + 1;
incX = 0;
incY = 1;
}
else
{
count = maxX - minX + 1;
incX = 1;
incY = 0;
}
U32 x = minX;
U32 y = minY;
for (U32 i = 0; i < count; i++)
{
U32 checkX = x % csmNumBins;
U32 checkY = y % csmNumBins;
SceneObjectRef* chain = mBinArray[(checkY * csmNumBins) + checkX].nextInBin;
while (chain)
{
SceneObject* ptr = chain->object;
if (ptr->getContainerSeqKey() != smCurrSeqKey)
{
ptr->setContainerSeqKey(smCurrSeqKey);
if ((ptr->getType() & mask) != 0 &&
ptr->isCollisionEnabled() == true)
{
if (ptr->getWorldBox().collideLine(start, end) || chain->object->isGlobalBounds())
{
Point3F xformedStart, xformedEnd;
ptr->mWorldToObj.mulP(start, &xformedStart);
ptr->mWorldToObj.mulP(end, &xformedEnd);
xformedStart.convolveInverse(ptr->mObjScale);
xformedEnd.convolveInverse(ptr->mObjScale);
RayInfo ri;
if (ptr->castRay(xformedStart, xformedEnd, &ri))
{
if(ri.t < currentT)
{
*info = ri;
info->point.interpolate(start, end, info->t);
currentT = ri.t;
}
}
}
}
}
chain = chain->nextInBin;
}
x += incX;
y += incY;
}
}
else
{
// Oh well, let's earn our keep. We know that after the above conditional, we're
// going to cross at least one boundary, so that simplifies our job...
F32 currStartX = normalStart.x;
AssertFatal(currStartX != normalEnd.x, "This is going to cause problems in Container::castRay");
while (currStartX != normalEnd.x)
{
F32 currEndX = getMin(currStartX + csmTotalBinSize, normalEnd.x);
F32 currStartT = (currStartX - normalStart.x) / (normalEnd.x - normalStart.x);
F32 currEndT = (currEndX - normalStart.x) / (normalEnd.x - normalStart.x);
F32 y1 = normalStart.y + (normalEnd.y - normalStart.y) * currStartT;
F32 y2 = normalStart.y + (normalEnd.y - normalStart.y) * currEndT;
U32 subMinX, subMaxX;
getBinRange(currStartX, currEndX, subMinX, subMaxX);
F32 subStartX = currStartX;
F32 subEndX = currStartX;
if (currStartX < 0.0f)
subEndX -= mFmod(subEndX, csmBinSize);
else
subEndX += (csmBinSize - mFmod(subEndX, csmBinSize));
for (U32 currXBin = subMinX; currXBin <= subMaxX; currXBin++)
{
U32 checkX = currXBin % csmNumBins;
F32 subStartT = (subStartX - currStartX) / (currEndX - currStartX);
F32 subEndT = getMin(F32((subEndX - currStartX) / (currEndX - currStartX)), 1.f);
F32 subY1 = y1 + (y2 - y1) * subStartT;
F32 subY2 = y1 + (y2 - y1) * subEndT;
U32 newMinY, newMaxY;
getBinRange(getMin(subY1, subY2), getMax(subY1, subY2), newMinY, newMaxY);
for (U32 i = newMinY; i <= newMaxY; i++)
{
U32 checkY = i % csmNumBins;
SceneObjectRef* chain = mBinArray[(checkY * csmNumBins) + checkX].nextInBin;
while (chain)
{
SceneObject* ptr = chain->object;
if (ptr->getContainerSeqKey() != smCurrSeqKey)
{
ptr->setContainerSeqKey(smCurrSeqKey);
if ((ptr->getType() & mask) != 0 &&
ptr->isCollisionEnabled() == true)
{
if (ptr->getWorldBox().collideLine(start, end))
{
Point3F xformedStart, xformedEnd;
ptr->mWorldToObj.mulP(start, &xformedStart);
ptr->mWorldToObj.mulP(end, &xformedEnd);
xformedStart.convolveInverse(ptr->mObjScale);
xformedEnd.convolveInverse(ptr->mObjScale);
RayInfo ri;
if (ptr->castRay(xformedStart, xformedEnd, &ri))
{
if(ri.t < currentT)
{
*info = ri;
info->point.interpolate(start, end, info->t);
currentT = ri.t;
}
}
}
}
}
chain = chain->nextInBin;
}
}
subStartX = subEndX;
subEndX = getMin(subEndX + csmBinSize, currEndX);
}
currStartX = currEndX;
}
}
// Bump the normal into worldspace if appropriate.
if(currentT != 2)
{
PlaneF fakePlane;
fakePlane.x = info->normal.x;
fakePlane.y = info->normal.y;
fakePlane.z = info->normal.z;
fakePlane.d = 0;
PlaneF result;
mTransformPlane(info->object->getTransform(), info->object->getScale(), fakePlane, &result);
info->normal = result;
PROFILE_END();
return true;
}
else
{
// Do nothing and exit...
PROFILE_END();
return false;
}
}
// collide with the objects projected object box
bool Container::collideBox(const Point3F &start, const Point3F &end, U32 mask, RayInfo * info)
{
F32 currentT = 2;
for (Link* itr = mStart.next; itr != &mEnd; itr = itr->next)
{
SceneObject* ptr = static_cast<SceneObject*>(itr);
if (ptr->getType() & mask && !ptr->mCollisionCount)
{
Point3F xformedStart, xformedEnd;
ptr->mWorldToObj.mulP(start, &xformedStart);
ptr->mWorldToObj.mulP(end, &xformedEnd);
xformedStart.convolveInverse(ptr->mObjScale);
xformedEnd.convolveInverse(ptr->mObjScale);
RayInfo ri;
if(ptr->collideBox(xformedStart, xformedEnd, &ri))
{
if(ri.t < currentT)
{
*info = ri;
info->point.interpolate(start, end, info->t);
currentT = ri.t;
}
}
}
}
return currentT != 2;
}
//----------------------------------------------------------------------------
static void buildCallback(SceneObject* object,void *key)
{
Container::CallbackInfo* info = reinterpret_cast<Container::CallbackInfo*>(key);
object->buildPolyList(info->polyList,info->boundingBox,info->boundingSphere);
}
//----------------------------------------------------------------------------
bool Container::buildPolyList(const Box3F& box, U32 mask, AbstractPolyList* polyList,FindCallback callback,void *key)
{
CallbackInfo info;
info.boundingBox = box;
info.polyList = polyList;
info.key = key;
// Build bounding sphere
info.boundingSphere.center = (info.boundingBox.min + info.boundingBox.max) * 0.5;
VectorF bv = box.max - info.boundingSphere.center;
info.boundingSphere.radius = bv.len();
sPolyList = polyList;
findObjects(box,mask,callback? callback: buildCallback,&info);
return !polyList->isEmpty();
}
//----------------------------------------------------------------------------
bool Container::buildCollisionList(const Box3F& box,
const Point3F& start,
const Point3F& end,
const VectorF& velocity,
U32 mask,
CollisionList* collisionList,
FindCallback callback,
void * key,
const Box3F* queryExpansion)
{
VectorF vector = end - start;
if (mFabs(vector.x) + mFabs(vector.y) + mFabs(vector.z) == 0)
return false;
CallbackInfo info;
info.key = key;
// Polylist bounding box & sphere
info.boundingBox.min = info.boundingBox.max = start;
info.boundingBox.min.setMin(end);
info.boundingBox.max.setMax(end);
info.boundingBox.min += box.min;
info.boundingBox.max += box.max;
info.boundingSphere.center = (info.boundingBox.min + info.boundingBox.max) * 0.5;
VectorF bv = info.boundingBox.max - info.boundingSphere.center;
info.boundingSphere.radius = bv.len();
// Box polyhedron edges & planes normals are always the same,
// just need to fill in the vertices and plane.d
Point3F* point = &sBoxPolyhedron.pointList[0];
point[0].x = point[1].x = point[4].x = point[5].x = box.min.x + start.x;
point[0].y = point[3].y = point[4].y = point[7].y = box.min.y + start.y;
point[2].x = point[3].x = point[6].x = point[7].x = box.max.x + start.x;
point[1].y = point[2].y = point[5].y = point[6].y = box.max.y + start.y;
point[0].z = point[1].z = point[2].z = point[3].z = box.min.z + start.z;
point[4].z = point[5].z = point[6].z = point[7].z = box.max.z + start.z;
PlaneF* plane = &sBoxPolyhedron.planeList[0];
plane[0].d = point[0].x;
plane[3].d = point[0].y;
plane[4].d = point[0].z;
plane[1].d = -point[6].y;
plane[2].d = -point[6].x;
plane[5].d = -point[6].z;
// Extruded
sExtrudedPolyList.extrude(sBoxPolyhedron,vector);
sExtrudedPolyList.setVelocity(velocity);
sExtrudedPolyList.setCollisionList(collisionList);
if (velocity.isZero())
{
sExtrudedPolyList.clearInterestNormal();
} else
{
Point3F normVec = velocity;
normVec.normalize();
sExtrudedPolyList.setInterestNormal(normVec);
}
info.polyList = &sExtrudedPolyList;
// Optional expansion of the query box
Box3F queryBox = info.boundingBox;
if (queryExpansion)
{
queryBox.min += queryExpansion->min;
queryBox.max += queryExpansion->max;
}
// Query main database
findObjects(queryBox,mask,callback? callback: buildCallback,&info);
sExtrudedPolyList.adjustCollisionTime();
return collisionList->count != 0;
}
//----------------------------------------------------------------------------
bool Container::buildCollisionList(const Polyhedron& polyhedron,
const Point3F& start, const Point3F& end,
const VectorF& velocity,
U32 mask, CollisionList* collisionList,
FindCallback callback, void *key)
{
VectorF vector = end - start;
if (mFabs(vector.x) + mFabs(vector.y) + mFabs(vector.z) == 0)
return false;
CallbackInfo info;
info.key = key;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -