mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Revised approach involves creating a helper function within ShapeFactory to aid in devising the ShapeType to be used by an ShapeEntityItem for collision. The ShapeFactory is currently doing this for creating the actual Bullet Library collision shapes. ShapeEntityItem overrides its virtually inherited computeShapeInfo which in turn calls the new ShapeFactory helper function. ShapeEntityItem has a new memvar _collisionShapeType to cache its actual ShapeType used by the physics system. This memvar is returned via the getShapeType accessor which is expected to return an object's ShapeType. Note(s): This is similar to the original approach save translation between entity::Shape and ShapeType isn't tied to the EntityItemProperties shapeTypeNames or shapeType. This approach more directly solves the issue of getting the actual ShapeType used by the time it's needed to determine the bullet collision object type created when initializing the physic information. Translation of the ShapeEntityItem's entity::Shape to its ShapeType is handled by ShapeFactory which handles creating the bullet collision objects when setting up physics on the ShapeEntityItems. Known Issue(s): This doesn't compile. It appears that the Entity Library needs to know about the Physics Library. The naive attempt at providing that link failed to resolve all compilation issues. Current Error: C1083: Cannot open include file: btBulletDynamicsCommon.h: No such file or directory (C:\projects\cusack\libraries\entities\src\ShapeEntityItem.cpp) C:\projects\cusack\libraries\physics\src\ShapeFactory.h 15 1 entities modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities/CMakeLists.txt modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.h modified: libraries/physics/src/ShapeFactory.cpp modified: libraries/physics/src/ShapeFactory.h modified: libraries/physics/src/ShapeInfo.cpp modified: scripts/developer/tests/basicEntityTest/entitySpawner.js new file: scripts/developer/tests/basicEntityTest/shapeSpawner.js
260 lines
9.1 KiB
C++
260 lines
9.1 KiB
C++
//
|
|
// ShapeInfo.cpp
|
|
// libraries/physics/src
|
|
//
|
|
// Created by Andrew Meadows 2014.10.29
|
|
// Copyright 2014 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
|
|
#include "ShapeInfo.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include "NumericalConstants.h" // for MILLIMETERS_PER_METER
|
|
|
|
// Bullet doesn't support arbitrarily small shapes
|
|
const float MIN_HALF_EXTENT = 0.005f; // 0.5 cm
|
|
|
|
void ShapeInfo::clear() {
|
|
_url.clear();
|
|
_pointCollection.clear();
|
|
_triangleIndices.clear();
|
|
_halfExtents = glm::vec3(0.0f);
|
|
_offset = glm::vec3(0.0f);
|
|
_doubleHashKey.clear();
|
|
_type = SHAPE_TYPE_NONE;
|
|
}
|
|
|
|
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) {
|
|
_url = "";
|
|
_type = type;
|
|
setHalfExtents(halfExtents);
|
|
switch(type) {
|
|
case SHAPE_TYPE_NONE:
|
|
_halfExtents = glm::vec3(0.0f);
|
|
break;
|
|
case SHAPE_TYPE_BOX:
|
|
break;
|
|
case SHAPE_TYPE_SPHERE: {
|
|
float radius = glm::length(halfExtents) / SQUARE_ROOT_OF_3;
|
|
radius = glm::max(radius, MIN_HALF_EXTENT);
|
|
_halfExtents = glm::vec3(radius);
|
|
}
|
|
break;
|
|
case SHAPE_TYPE_COMPOUND:
|
|
case SHAPE_TYPE_STATIC_MESH:
|
|
_url = QUrl(url);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
_doubleHashKey.clear();
|
|
}
|
|
|
|
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
|
|
_url = "";
|
|
_type = SHAPE_TYPE_BOX;
|
|
setHalfExtents(halfExtents);
|
|
_doubleHashKey.clear();
|
|
}
|
|
|
|
void ShapeInfo::setSphere(float radius) {
|
|
_url = "";
|
|
_type = SHAPE_TYPE_SPHERE;
|
|
radius = glm::max(radius, MIN_HALF_EXTENT);
|
|
_halfExtents = glm::vec3(radius);
|
|
_doubleHashKey.clear();
|
|
}
|
|
|
|
void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) {
|
|
_pointCollection = pointCollection;
|
|
_type = (_pointCollection.size() > 0) ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE;
|
|
_doubleHashKey.clear();
|
|
}
|
|
|
|
void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
|
|
_url = "";
|
|
_type = SHAPE_TYPE_CAPSULE_Y;
|
|
radius = glm::max(radius, MIN_HALF_EXTENT);
|
|
halfHeight = glm::max(halfHeight, 0.0f);
|
|
_halfExtents = glm::vec3(radius, halfHeight, radius);
|
|
_doubleHashKey.clear();
|
|
}
|
|
|
|
void ShapeInfo::setOffset(const glm::vec3& offset) {
|
|
_offset = offset;
|
|
_doubleHashKey.clear();
|
|
}
|
|
|
|
uint32_t ShapeInfo::getNumSubShapes() const {
|
|
switch (_type) {
|
|
case SHAPE_TYPE_NONE:
|
|
return 0;
|
|
case SHAPE_TYPE_COMPOUND:
|
|
case SHAPE_TYPE_SIMPLE_COMPOUND:
|
|
return _pointCollection.size();
|
|
case SHAPE_TYPE_SIMPLE_HULL:
|
|
case SHAPE_TYPE_STATIC_MESH:
|
|
assert(_pointCollection.size() == 1);
|
|
// yes fall through to default
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int ShapeInfo::getLargestSubshapePointCount() const {
|
|
int numPoints = 0;
|
|
for (int i = 0; i < _pointCollection.size(); ++i) {
|
|
int n = _pointCollection[i].size();
|
|
if (n > numPoints) {
|
|
numPoints = n;
|
|
}
|
|
}
|
|
return numPoints;
|
|
}
|
|
|
|
float ShapeInfo::computeVolume() const {
|
|
const float DEFAULT_VOLUME = 1.0f;
|
|
float volume = DEFAULT_VOLUME;
|
|
switch(_type) {
|
|
case SHAPE_TYPE_BOX: {
|
|
// factor of 8.0 because the components of _halfExtents are all halfExtents
|
|
volume = 8.0f * _halfExtents.x * _halfExtents.y * _halfExtents.z;
|
|
break;
|
|
}
|
|
case SHAPE_TYPE_SPHERE: {
|
|
volume = 4.0f * PI * _halfExtents.x * _halfExtents.y * _halfExtents.z / 3.0f;
|
|
break;
|
|
}
|
|
case SHAPE_TYPE_CYLINDER_Y: {
|
|
float radius = _halfExtents.x;
|
|
volume = PI * radius * radius * 2.0f * _halfExtents.y;
|
|
break;
|
|
}
|
|
case SHAPE_TYPE_CAPSULE_Y: {
|
|
float radius = _halfExtents.x;
|
|
// Need to offset halfExtents.y by x to account for the system treating
|
|
// the y extent of the capsule as the cylindrical height + spherical radius.
|
|
float cylinderHeight = 2.0f * (_halfExtents.y - _halfExtents.x);
|
|
volume = PI * radius * radius * (cylinderHeight + 4.0f * radius / 3.0f);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
assert(volume > 0.0f);
|
|
return volume;
|
|
}
|
|
|
|
bool ShapeInfo::contains(const glm::vec3& point) const {
|
|
switch(_type) {
|
|
case SHAPE_TYPE_SPHERE:
|
|
return glm::length(point) <= _halfExtents.x;
|
|
case SHAPE_TYPE_CYLINDER_X:
|
|
return glm::length(glm::vec2(point.y, point.z)) <= _halfExtents.z;
|
|
case SHAPE_TYPE_CYLINDER_Y:
|
|
return glm::length(glm::vec2(point.x, point.z)) <= _halfExtents.x;
|
|
case SHAPE_TYPE_CYLINDER_Z:
|
|
return glm::length(glm::vec2(point.x, point.y)) <= _halfExtents.y;
|
|
case SHAPE_TYPE_CAPSULE_X: {
|
|
if (glm::abs(point.x) <= _halfExtents.x) {
|
|
return glm::length(glm::vec2(point.y, point.z)) <= _halfExtents.z;
|
|
} else {
|
|
glm::vec3 absPoint = glm::abs(point) - _halfExtents.x;
|
|
return glm::length(absPoint) <= _halfExtents.z;
|
|
}
|
|
}
|
|
case SHAPE_TYPE_CAPSULE_Y: {
|
|
if (glm::abs(point.y) <= _halfExtents.y) {
|
|
return glm::length(glm::vec2(point.x, point.z)) <= _halfExtents.x;
|
|
} else {
|
|
glm::vec3 absPoint = glm::abs(point) - _halfExtents.y;
|
|
return glm::length(absPoint) <= _halfExtents.x;
|
|
}
|
|
}
|
|
case SHAPE_TYPE_CAPSULE_Z: {
|
|
if (glm::abs(point.z) <= _halfExtents.z) {
|
|
return glm::length(glm::vec2(point.x, point.y)) <= _halfExtents.y;
|
|
} else {
|
|
glm::vec3 absPoint = glm::abs(point) - _halfExtents.z;
|
|
return glm::length(absPoint) <= _halfExtents.y;
|
|
}
|
|
}
|
|
case SHAPE_TYPE_BOX:
|
|
default: {
|
|
glm::vec3 absPoint = glm::abs(point);
|
|
return absPoint.x <= _halfExtents.x
|
|
&& absPoint.y <= _halfExtents.y
|
|
&& absPoint.z <= _halfExtents.z;
|
|
}
|
|
}
|
|
}
|
|
|
|
const DoubleHashKey& ShapeInfo::getHash() const {
|
|
// NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance.
|
|
if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) {
|
|
bool useOffset = glm::length2(_offset) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET;
|
|
// The key is not yet cached therefore we must compute it.
|
|
|
|
// compute hash1
|
|
// TODO?: provide lookup table for hash/hash2 of _type rather than recompute?
|
|
uint32_t primeIndex = 0;
|
|
_doubleHashKey.computeHash((uint32_t)_type, primeIndex++);
|
|
|
|
// compute hash1
|
|
uint32_t hash = _doubleHashKey.getHash();
|
|
for (int j = 0; j < 3; ++j) {
|
|
// NOTE: 0.49f is used to bump the float up almost half a millimeter
|
|
// so the cast to int produces a round() effect rather than a floor()
|
|
hash ^= DoubleHashKey::hashFunction(
|
|
(uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f),
|
|
primeIndex++);
|
|
if (useOffset) {
|
|
hash ^= DoubleHashKey::hashFunction(
|
|
(uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f),
|
|
primeIndex++);
|
|
}
|
|
}
|
|
_doubleHashKey.setHash(hash);
|
|
|
|
// compute hash2
|
|
hash = _doubleHashKey.getHash2();
|
|
for (int j = 0; j < 3; ++j) {
|
|
// NOTE: 0.49f is used to bump the float up almost half a millimeter
|
|
// so the cast to int produces a round() effect rather than a floor()
|
|
uint32_t floatHash = DoubleHashKey::hashFunction2(
|
|
(uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f));
|
|
if (useOffset) {
|
|
floatHash ^= DoubleHashKey::hashFunction2(
|
|
(uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f));
|
|
}
|
|
hash += ~(floatHash << 17);
|
|
hash ^= (floatHash >> 11);
|
|
hash += (floatHash << 4);
|
|
hash ^= (floatHash >> 7);
|
|
hash += ~(floatHash << 10);
|
|
hash = (hash << 16) | (hash >> 16);
|
|
}
|
|
_doubleHashKey.setHash2(hash);
|
|
|
|
if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_STATIC_MESH) {
|
|
QString url = _url.toString();
|
|
if (!url.isEmpty()) {
|
|
// fold the urlHash into both parts
|
|
QByteArray baUrl = url.toLocal8Bit();
|
|
const char *cUrl = baUrl.data();
|
|
uint32_t urlHash = qChecksum(cUrl, baUrl.count());
|
|
_doubleHashKey.setHash(_doubleHashKey.getHash() ^ urlHash);
|
|
_doubleHashKey.setHash2(_doubleHashKey.getHash2() ^ urlHash);
|
|
}
|
|
}
|
|
}
|
|
return _doubleHashKey;
|
|
}
|
|
|
|
void ShapeInfo::setHalfExtents(const glm::vec3& halfExtents) {
|
|
_halfExtents = glm::max(halfExtents, glm::vec3(MIN_HALF_EXTENT));
|
|
}
|