mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
437 lines
10 KiB
C++
437 lines
10 KiB
C++
#include "stdafx.h"
|
|
|
|
#include "V8World/Block.h"
|
|
#include "V8World/BlockCorners.h"
|
|
#include "V8World/BlockMesh.h"
|
|
#include "Util/Math.h"
|
|
#include "G3D/CollisionDetection.h"
|
|
|
|
namespace RBX {
|
|
|
|
using namespace POLY;
|
|
|
|
/** VERTICES are in x,y,z order, so polarity is
|
|
vertID x,y,z
|
|
0 1,1,1
|
|
1 1,1,-1
|
|
2 1,-1,1
|
|
3 1,-1,-1
|
|
4 -1,1,1
|
|
5 -1,1,-1
|
|
6 -1,-1,1
|
|
7 -1,-1,-1
|
|
*/
|
|
|
|
/** FACES / PLANEIDS
|
|
0 +x
|
|
1 +y
|
|
2 +z
|
|
3 -x
|
|
4 -y
|
|
5 -z
|
|
*/
|
|
|
|
/** EDGE ID's
|
|
Same as faceID's - i.e., there are 12 edges, they correspond to the
|
|
first three faces (+x,+y,+z) as the normal direction (NORM_X, NORM_Y, NORM_Z)
|
|
along with a vertex from the face
|
|
|
|
Every other normal is negative so that the normals on any different face (4 edges) are all
|
|
unique
|
|
|
|
EDGE Normal Vertex
|
|
0 X 0
|
|
1 -X 2
|
|
2 X 3
|
|
3 -X 1
|
|
4 Y 0
|
|
5 -Y 1
|
|
6 Y 5
|
|
7 -Y 4
|
|
8 Z 0
|
|
9 -Z 4
|
|
10 Z 6
|
|
11 -Z 2
|
|
*/
|
|
|
|
/** VERTEX ORDERING ON FACES
|
|
Vertexes are ordered so:
|
|
1. They proceed counter-clockwise about the face
|
|
2. The first zero is in the "+,+" position for the positive plane orientation
|
|
|
|
x+ plane: y,z coords (y right, z up)
|
|
y+ plane: z,x coords (z right, x up)
|
|
z+ plane: x,y coords (x right, y up)
|
|
|
|
-x plane: z,y coordinates
|
|
-y plane: x,z coordinates
|
|
-z plane: y,x coordinates
|
|
*/
|
|
|
|
const int Block::BLOCK_FACE_TO_VERTEX[6][4] =
|
|
{
|
|
0,2,3,1, // x+
|
|
0,1,5,4, // y+
|
|
0,4,6,2, // z+
|
|
4,5,7,6, // x-
|
|
2,6,7,3, // y-
|
|
1,3,7,5 // z-
|
|
};
|
|
|
|
|
|
/** Gives the edge that connects this vertex with the next one on the face,
|
|
in counter-clockwise order
|
|
Index Face Vertex Edge EdgeVertex EdgeID
|
|
0 x 0 Y 0 4
|
|
1 x 2 Z 2 11
|
|
2 x 3 Y 1 5
|
|
3 x 1 Z 0 8
|
|
4 y 0 Z 0 8
|
|
5 y 1 X 1 3
|
|
6 y 5 Z 4 9
|
|
7 y 4 X 0 0
|
|
8 z 0 X 0 0
|
|
9 z 4 Y 4 7
|
|
10 z 6 X 2 1
|
|
11 z 2 Y 0 4
|
|
|
|
12 -x 4 z 4 9
|
|
13 -x 5 y 5 6
|
|
14 -x 7 z 6 10
|
|
15 -x 6 y 4 7
|
|
16 -y 2 x 2 1
|
|
17 -y 6 z 6 10
|
|
18 -y 7 x 3 2
|
|
19 -y 3 z 2 11
|
|
20 -z 1 y 1 5
|
|
21 -z 3 x 3 2
|
|
22 -z 7 y 5 6
|
|
23 -z 5 x 1 3
|
|
*/
|
|
|
|
const int Block::BLOCK_FACE_VERTEX_TO_EDGE[6][4] =
|
|
{
|
|
4,11,5,8,
|
|
8,3,9,0,
|
|
0,7,1,4,
|
|
9,6,10,7,
|
|
1,10,2,11,
|
|
5,2,6,3
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void Block::init()
|
|
{
|
|
BlockMeshPool::init();
|
|
BlockCornersPool::init();
|
|
|
|
// Make sure we have token references for the default part size so that it never goes away
|
|
// This resolves the issue of slow Block structure construction happening every time we create a part
|
|
Vector3 initialSize = Vector3(4.0f, 1.2f, 2.0f);
|
|
|
|
static BlockMeshPool::Token meshToken = BlockMeshPool::getToken(initialSize);
|
|
static BlockCornersPool::Token cornersToken = BlockCornersPool::getToken(initialSize);
|
|
}
|
|
|
|
void Block::buildMesh()
|
|
{
|
|
Vector3 key = getSize();
|
|
blockMesh = BlockMeshPool::getToken(key);
|
|
mesh = blockMesh->getMesh();
|
|
}
|
|
|
|
|
|
void Block::setSize(const G3D::Vector3& _size)
|
|
{
|
|
Super::setSize(_size);
|
|
RBXASSERT(_size == getSize());
|
|
|
|
Vector3 key = getSize() * 0.5f;
|
|
|
|
blockCorners = BlockCornersPool::getToken(key);
|
|
vertices = blockCorners->getVertices();
|
|
|
|
if (bulletCollisionObject)
|
|
updateBulletCollisionData();
|
|
}
|
|
|
|
|
|
/*
|
|
Matrix3 Block::getMomentSolid(float mass) const
|
|
{
|
|
Vector3 size = getSize();
|
|
float c = mass / 12.0f;
|
|
|
|
// size if from one edge to the other
|
|
Vector3 answer =
|
|
Vector3(
|
|
c * (size.y * size.y + size.z * size.z),
|
|
c * (size.x * size.x + size.z * size.z),
|
|
c * (size.x * size.x + size.y * size.y)
|
|
);
|
|
|
|
return Math::fromDiagonal(answer);
|
|
}
|
|
*/
|
|
|
|
// See scanned calulations in V8 Technical Doc
|
|
Matrix3 Block::getMomentHollow(float mass) const
|
|
{
|
|
Vector3 size = getSize();
|
|
|
|
float area = 2 * (size.x * size.y + size.y * size.z + size.z * size.x);
|
|
Vector3 I;
|
|
for (int i = 0; i < 3; i++) {
|
|
int j = (i + 1) % 3;
|
|
int k = (i + 2) % 3;
|
|
float x = size[i]; // main axis;
|
|
float y = size[j];
|
|
float z = size[k];
|
|
|
|
float Ix = (mass / (2.0f * area)) * ( (y*y*y*z/3.0f)
|
|
+ (y*z*z*z/3.0f)
|
|
+ (x*y*z*z)
|
|
+ (x*y*y*y/3.0f)
|
|
+ (x*y*y*z)
|
|
+ (x*z*z*z/3.0f) );
|
|
I[i] = Ix;
|
|
}
|
|
return Math::fromDiagonal(I);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool Block::hitTest(const RbxRay& rayInMe, Vector3& localHitPoint, Vector3& surfaceNormal)
|
|
{
|
|
Vector3 halfRealSize = getSize() * 0.5;
|
|
|
|
bool inside = false;
|
|
return G3D::CollisionDetection::collisionLocationForMovingPointFixedAABox(
|
|
rayInMe.origin(),
|
|
rayInMe.direction(),
|
|
AABox(-halfRealSize, halfRealSize),
|
|
localHitPoint,
|
|
inside,
|
|
surfaceNormal);
|
|
}
|
|
|
|
Vector3 Block::getCenterToCorner(const Matrix3& rotation) const
|
|
{
|
|
Vector3 maxValue = G3D::abs(rotation * vertices[0]);
|
|
for (int i = 1; i < 4; ++i) {
|
|
maxValue = maxValue.max(G3D::abs(rotation * vertices[i]));
|
|
}
|
|
return maxValue;
|
|
}
|
|
|
|
|
|
|
|
float Block::getVolume() const
|
|
{
|
|
Vector3 size = getSize();
|
|
return size.x * size.y * size.z;
|
|
}
|
|
|
|
|
|
const Vector3* Block::getCornerPoint(const Vector3int16& clip) const {
|
|
int x = clip.x > 0 ? 0 : 1;
|
|
int y = clip.y > 0 ? 0 : 1;
|
|
int z = clip.z > 0 ? 0 : 1;
|
|
|
|
return &vertices[x*4 + y*2 + z];
|
|
}
|
|
|
|
|
|
const Vector3* Block::getEdgePoint(const Vector3int16& clip, RBX::NormalId& normalID) const {
|
|
|
|
// normal is from the negative "zero" point positive
|
|
if (clip.x == 0) {
|
|
normalID = RBX::NORM_X;
|
|
int y = clip.y > 0 ? 0 : 1;
|
|
int z = clip.z > 0 ? 0 : 1;
|
|
return &vertices[4 + y*2 + z];
|
|
}
|
|
if (clip.y == 0) {
|
|
normalID = RBX::NORM_Y;
|
|
int x = clip.x > 0 ? 0 : 1;
|
|
int z = clip.z > 0 ? 0 : 1;
|
|
return &vertices[x*4 + 2 + z];
|
|
}
|
|
RBXASSERT(!clip.z);
|
|
{
|
|
normalID = RBX::NORM_Z;
|
|
int x = clip.x > 0 ? 0 : 1;
|
|
int y = clip.y > 0 ? 0 : 1;
|
|
return &vertices[x*4 + y*2 + 1];
|
|
}
|
|
}
|
|
|
|
|
|
const Vector3* Block::getPlanePoint(const Vector3int16& clip, RBX::NormalId& normalID) const {
|
|
|
|
// normal is from the negative "zero" point positive
|
|
if (clip.x != 0) {
|
|
normalID = clip.x > 0 ? RBX::NORM_X : RBX::NORM_X_NEG;
|
|
int x = clip.x > 0 ? 0 : 1;
|
|
return &vertices[x*4]; // either +x,+y,+z or -x,+y,+z
|
|
}
|
|
if (clip.y != 0) {
|
|
normalID = clip.y > 0 ? RBX::NORM_Y : RBX::NORM_Y_NEG;
|
|
int y = clip.y > 0 ? 0 : 1;
|
|
return &vertices[y*2];
|
|
}
|
|
RBXASSERT(clip.z);
|
|
{
|
|
normalID = clip.z > 0 ? RBX::NORM_Z : RBX::NORM_Z_NEG;
|
|
int z = clip.z > 0 ? 0 : 1;
|
|
return &vertices[z];
|
|
}
|
|
}
|
|
|
|
GeoPairType Block::getBallBlockInfo(int onBorder, const Vector3int16 clip, const Vector3* &offset,
|
|
RBX::NormalId& normalID) {
|
|
|
|
|
|
// ball plane - only clipped to one plane
|
|
if (onBorder == 1) {
|
|
offset = getPlanePoint(clip, normalID);
|
|
return BALL_PLANE_PAIR;
|
|
}
|
|
else {
|
|
// ball edge - clipped to two planes
|
|
if (onBorder == 2) {
|
|
offset = getEdgePoint(clip, normalID);
|
|
return BALL_EDGE_PAIR;
|
|
}
|
|
// ball point - clipped to three planes
|
|
else {
|
|
offset = getCornerPoint(clip);
|
|
return BALL_POINT_PAIR;
|
|
}
|
|
}
|
|
}
|
|
|
|
GeoPairType Block::getBallInsideInfo(const Vector3& ray, const Vector3* &offset, RBX::NormalId& normalID)
|
|
{
|
|
float min = FLT_MAX; // used to be inf() - slow compares?
|
|
const Vector3& l = vertices[0]; // all positive;
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
float temp = l[i] - ray[i];
|
|
if (temp < min) {
|
|
min = temp;
|
|
normalID = static_cast<RBX::NormalId>(i);
|
|
}
|
|
temp = ray[i] + l[i];
|
|
if (temp < min) {
|
|
min = temp;
|
|
normalID = static_cast<RBX::NormalId>(i+3);
|
|
}
|
|
}
|
|
RBXASSERT(min != FLT_MAX);
|
|
|
|
offset = (normalID > RBX::NORM_Z) ? &vertices[7] : &vertices[0];
|
|
return BALL_PLANE_PAIR;
|
|
}
|
|
|
|
// needs to handle inside and outside point
|
|
void Block::projectToFace(Vector3& ray, Vector3int16& clip, int& onBorder)
|
|
{
|
|
onBorder = 0;
|
|
const Vector3& l = vertices[0]; // all positive;
|
|
|
|
if (ray.x > l.x) {ray.x = l.x; onBorder++; clip.x = 1;}
|
|
if (ray.x < -l.x) {ray.x = -l.x; onBorder++; clip.x = -1;}
|
|
|
|
if (ray.y > l.y) {ray.y = l.y; onBorder++; clip.y = 1;}
|
|
if (ray.y < -l.y) {ray.y = -l.y; onBorder++; clip.y = -1;}
|
|
|
|
if (ray.z > l.z) {ray.z = l.z; onBorder++; clip.z = 1;}
|
|
if (ray.z < -l.z) {ray.z = -l.z; onBorder++; clip.z = -1;}
|
|
}
|
|
|
|
// for +x plane: y,z coordinates
|
|
// for +y plane: z,x coordinates
|
|
// for +z plane: x,y coordinates
|
|
// for -x plane: z,y coordinates
|
|
// for -y plane: x,z coordinates
|
|
// for -z plane: y,x coordinates
|
|
|
|
|
|
Vector2 Block::getProjectedVertex(const Vector3& vertex, RBX::NormalId normalID) {
|
|
|
|
Vector2 ans;
|
|
switch (normalID) {
|
|
case (RBX::NORM_X): ans.x = vertex.y; ans.y = vertex.z; return ans;
|
|
case (RBX::NORM_Y): ans.x = vertex.z; ans.y = vertex.x; return ans;
|
|
case (RBX::NORM_Z): ans.x = vertex.x; ans.y = vertex.y; return ans;
|
|
case (RBX::NORM_X_NEG): ans.x = vertex.z; ans.y = vertex.y; return ans;
|
|
case (RBX::NORM_Y_NEG): ans.x = vertex.x; ans.y = vertex.z; return ans;
|
|
case (RBX::NORM_Z_NEG): ans.x = vertex.y; ans.y = vertex.x; return ans;
|
|
|
|
// suppress compiler warning
|
|
default: return ans;
|
|
}
|
|
}
|
|
|
|
|
|
int Block::getClosestEdge(const Matrix3& rotation, NormalId normalID, const Vector3& crossAxis)
|
|
{
|
|
Vector3 axisInBody = Math::vectorToObjectSpace(crossAxis, rotation);
|
|
|
|
Vector2 projected = getProjectedVertex(axisInBody, normalID);
|
|
|
|
if (projected.y > 0) {
|
|
if (projected.x > 0) { return normalID*4; }
|
|
else { return normalID*4 + 1; }
|
|
}
|
|
else {
|
|
if (projected.x > 0) { return normalID*4 + 3; }
|
|
else { return normalID*4 + 2; }
|
|
}
|
|
}
|
|
|
|
CoordinateFrame Block::getSurfaceCoordInBody( const size_t surfaceId ) const
|
|
{
|
|
CoordinateFrame aCS;
|
|
|
|
// the face reference coord origin is the midpoint b/t the 2 and 3 vertex (so this is the base of the side faces)
|
|
//aCS.translation = 0.5 * (mesh->getFace(surfaceId)->getVertex(2)->getOffset() + mesh->getFace(surfaceId)->getVertex(3)->getOffset());
|
|
|
|
// Alternative for origin to preserve pre-existing block to block grid snapping.
|
|
// That is, don't snap to center of face's base edge, snap to the end point.
|
|
// However, this will prevent symmetry when snapping block to special shape (i.e. prism)
|
|
aCS.translation = mesh->getFace(surfaceId)->getVertex(2)->getOffset();
|
|
aCS.rotation = Math::getWellFormedRotForZVector(mesh->getFace(surfaceId)->normal());
|
|
|
|
return aCS;
|
|
}
|
|
|
|
bool Block::setUpBulletCollisionData(void)
|
|
{
|
|
if (!bulletCollisionObject)
|
|
updateBulletCollisionData();
|
|
|
|
return true;
|
|
}
|
|
|
|
void Block::updateBulletCollisionData()
|
|
{
|
|
if (!bulletCollisionObject)
|
|
bulletCollisionObject.reset(new btCollisionObject());
|
|
|
|
bulletBoxShape = BulletBoxShapePool::getToken(getSize());
|
|
bulletCollisionObject->setCollisionShape(const_cast<btBoxShape*>(bulletBoxShape->getShape()));
|
|
}
|
|
|
|
} // namespace
|