ShapeInfo computs own DoubleHashKey, gets new API

This commit is contained in:
Andrew Meadows 2015-02-08 15:12:44 -08:00
parent 13b00f3f8f
commit 12a7021bb5
7 changed files with 288 additions and 147 deletions

View file

@ -9,7 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <ShapeInfo.h>
#include <SharedUtil.h> // for MILLIMETERS_PER_METER
#include "ShapeInfoUtil.h"
@ -24,12 +23,9 @@ int ShapeInfoUtil::toBulletShapeType(int shapeInfoType) {
case SHAPE_TYPE_SPHERE:
bulletShapeType = SPHERE_SHAPE_PROXYTYPE;
break;
case SHAPE_TYPE_CAPSULE:
case SHAPE_TYPE_CAPSULE_Y:
bulletShapeType = CAPSULE_SHAPE_PROXYTYPE;
break;
case SHAPE_TYPE_CYLINDER:
bulletShapeType = CYLINDER_SHAPE_PROXYTYPE;
break;
}
return bulletShapeType;
}
@ -44,10 +40,7 @@ int ShapeInfoUtil::fromBulletShapeType(int bulletShapeType) {
shapeInfoType = SHAPE_TYPE_SPHERE;
break;
case CAPSULE_SHAPE_PROXYTYPE:
shapeInfoType = SHAPE_TYPE_CAPSULE;
break;
case CYLINDER_SHAPE_PROXYTYPE:
shapeInfoType = SHAPE_TYPE_CYLINDER;
shapeInfoType = SHAPE_TYPE_CAPSULE_Y;
break;
}
return shapeInfoType;
@ -67,19 +60,6 @@ void ShapeInfoUtil::collectInfoFromShape(const btCollisionShape* shape, ShapeInf
info.setSphere(sphereShape->getRadius());
}
break;
case SHAPE_TYPE_CYLINDER: {
// NOTE: we only support cylinders along yAxis
const btCylinderShape* cylinderShape = static_cast<const btCylinderShape*>(shape);
btVector3 halfExtents = cylinderShape->getHalfExtentsWithMargin();
info.setCylinder(halfExtents.getX(), halfExtents.getY());
}
break;
case SHAPE_TYPE_CAPSULE: {
// NOTE: we only support capsules along yAxis
const btCapsuleShape* capsuleShape = static_cast<const btCapsuleShape*>(shape);
info.setCapsule(capsuleShape->getRadius(), capsuleShape->getHalfHeight());
}
break;
default:
info.clear();
break;
@ -91,27 +71,20 @@ void ShapeInfoUtil::collectInfoFromShape(const btCollisionShape* shape, ShapeInf
btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) {
btCollisionShape* shape = NULL;
const QVector<glm::vec3>& data = info.getData();
switch(info.getType()) {
case SHAPE_TYPE_BOX: {
// data[0] is halfExtents
shape = new btBoxShape(glmToBullet(data[0]));
shape = new btBoxShape(glmToBullet(info.getHalfExtents()));
}
break;
case SHAPE_TYPE_SPHERE: {
float radius = data[0].z;
float radius = info.getHalfExtents().x;
shape = new btSphereShape(radius);
}
break;
case SHAPE_TYPE_CYLINDER: {
// NOTE: default cylinder has (UpAxis = 1) axis along yAxis and radius stored in X
// data[0] = btVector3(radius, halfHeight, unused)
shape = new btCylinderShape(glmToBullet(data[0]));
}
break;
case SHAPE_TYPE_CAPSULE: {
float radius = data[0].x;
float height = 2.0f * data[0].y;
case SHAPE_TYPE_CAPSULE_Y: {
glm::vec3 halfExtents = info.getHalfExtents();
float radius = halfExtents.x;
float height = 2.0f * halfExtents.y;
shape = new btCapsuleShape(radius, height);
}
break;
@ -119,49 +92,85 @@ btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) {
return shape;
}
DoubleHashKey ShapeInfoUtil::computeHash(const ShapeInfo& info) {
DoubleHashKey key;
// compute hash
// scramble the bits of the type
// TODO?: provide lookup table for hash of info._type rather than recompute?
int primeIndex = 0;
unsigned int hash = DoubleHashKey::hashFunction((unsigned int)info.getType(), primeIndex++);
const QVector<glm::vec3>& data = info.getData();
/*
const DoubleHashKey& ShapeInfo::computeHash() {
if (_hash.isNull()) {
// compute hash1
// TODO?: provide lookup table for hash/hash2 of _type rather than recompute?
int primeIndex = 0;
_doubleHashKey._hash = DoubleHashKey::hashFunction((uint32_t)_type, primeIndex++);
_doubleHashKey._hash2 = DoubleHashKey::hashFunction2((uint32_t)_type());
if (getData()) {
// if externalData exists we use that to continue the hash
glm::vec3 tmpData;
int numData = data.size();
for (int i = 0; i < numData; ++i) {
tmpData = data[i];
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()
unsigned int floatHash =
DoubleHashKey::hashFunction((int)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f), primeIndex++);
hash ^= floatHash;
// compute hash
uint32_t hash = _doubleHashKey._hash;
const QVector<glm::vec3>& data = getData();
glm::vec3 tmpData;
int numData = data.size();
for (int i = 0; i < numData; ++i) {
tmpData = data[i];
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::hashFunction((uint32_t)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f), primeIndex++);
hash ^= floatHash;
}
}
_doubleHashKey._hash = (int32_t)hash;
// compute hash2
hash = _doubleHashKey._hash2;
for (int i = 0; i < numData; ++i) {
tmpData = data[i];
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)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f));
hash += ~(floatHash << 17);
hash ^= (floatHash >> 11);
hash += (floatHash << 4);
hash ^= (floatHash >> 7);
hash += ~(floatHash << 10);
hash = (hash << 16) | (hash >> 16);
}
}
_doubleHashKey._hash2 = (uint32_t)hash;
} else {
// this shape info has no external data so we just use extents to continue hash
// compute hash1
uint32_t hash = _doubleHashKey._hash;
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::hashFunction((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f), primeIndex++);
hash ^= floatHash;
}
_doubleHashKey._hash = (uint32_t)hash;
// compute hash2
hash = _doubleHashKey._hash2;
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));
hash += ~(floatHash << 17);
hash ^= (floatHash >> 11);
hash += (floatHash << 4);
hash ^= (floatHash >> 7);
hash += ~(floatHash << 10);
hash = (hash << 16) | (hash >> 16);
}
_doubleHashKey._hash2 = (uint32_t)hash;
}
}
key._hash = (int)hash;
// compute hash2
// scramble the bits of the type
// TODO?: provide lookup table for hash2 of info._type rather than recompute?
hash = DoubleHashKey::hashFunction2((unsigned int)info.getType());
for (int i = 0; i < numData; ++i) {
tmpData = data[i];
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()
unsigned int floatHash =
DoubleHashKey::hashFunction2((int)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f));
hash += ~(floatHash << 17);
hash ^= (floatHash >> 11);
hash += (floatHash << 4);
hash ^= (floatHash >> 7);
hash += ~(floatHash << 10);
hash = (hash << 16) | (hash >> 16);
}
}
key._hash2 = (int)hash;
return key;
return _doubleHashKey;
}
*/

View file

@ -15,10 +15,9 @@
#include <btBulletDynamicsCommon.h>
#include <glm/glm.hpp>
#include <DoubleHashKey.h>
#include <ShapeInfo.h>
#include "DoubleHashKey.h"
// translates between ShapeInfo and btShape
namespace ShapeInfoUtil {
@ -26,7 +25,7 @@ namespace ShapeInfoUtil {
btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
DoubleHashKey computeHash(const ShapeInfo& info);
//DoubleHashKey computeHash(const ShapeInfo& info);
// TODO? just use bullet shape types everywhere?
int toBulletShapeType(int shapeInfoType);

View file

@ -28,13 +28,13 @@ ShapeManager::~ShapeManager() {
btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
// Very small or large objects are not supported.
float diagonal = glm::length2(info.getBoundingBoxDiagonal());
float diagonal = 4.0f * glm::length2(info.getHalfExtents());
const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
const float MAX_SHAPE_DIAGONAL_SQUARED = 3.0e4f; // 100 m cube
if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED || diagonal > MAX_SHAPE_DIAGONAL_SQUARED) {
return NULL;
}
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
DoubleHashKey key = info.getHash();
ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef) {
shapeRef->_refCount++;
@ -51,7 +51,7 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
}
bool ShapeManager::releaseShape(const ShapeInfo& info) {
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
DoubleHashKey key = info.getHash();
ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef) {
if (shapeRef->_refCount > 0) {
@ -95,7 +95,7 @@ void ShapeManager::collectGarbage() {
}
int ShapeManager::getNumReferences(const ShapeInfo& info) const {
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
DoubleHashKey key = info.getHash();
const ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef) {
return shapeRef->_refCount;

View file

@ -1,6 +1,6 @@
//
// DoubleHashKey.cpp
// libraries/physcis/src
// libraries/shared/src
//
// Created by Andrew Meadows 2014.11.02
// Copyright 2014 High Fidelity, Inc.
@ -11,8 +11,8 @@
#include "DoubleHashKey.h"
const int NUM_PRIMES = 64;
const unsigned int PRIMES[] = {
const uint32_t NUM_PRIMES = 64;
const uint32_t PRIMES[] = {
4194301U, 4194287U, 4194277U, 4194271U, 4194247U, 4194217U, 4194199U, 4194191U,
4194187U, 4194181U, 4194173U, 4194167U, 4194143U, 4194137U, 4194131U, 4194107U,
4194103U, 4194023U, 4194011U, 4194007U, 4193977U, 4193971U, 4193963U, 4193957U,
@ -23,8 +23,8 @@ const unsigned int PRIMES[] = {
4193353U, 4193327U, 4193309U, 4193303U, 4193297U, 4193279U, 4193269U, 4193263U
};
unsigned int DoubleHashKey::hashFunction(unsigned int value, int primeIndex) {
unsigned int hash = PRIMES[primeIndex % NUM_PRIMES] * (value + 1U);
uint32_t DoubleHashKey::hashFunction(uint32_t value, uint32_t primeIndex) {
uint32_t hash = PRIMES[primeIndex % NUM_PRIMES] * (value + 1U);
hash += ~(hash << 15);
hash ^= (hash >> 10);
hash += (hash << 3);
@ -33,11 +33,16 @@ unsigned int DoubleHashKey::hashFunction(unsigned int value, int primeIndex) {
return hash ^ (hash >> 16);
}
unsigned int DoubleHashKey::hashFunction2(unsigned int value) {
unsigned hash = 0x811c9dc5U;
for (int i = 0; i < 4; i++ ) {
unsigned int byte = (value << (i * 8)) >> (24 - i * 8);
uint32_t DoubleHashKey::hashFunction2(uint32_t value) {
uint32_t hash = 0x811c9dc5U;
for (uint32_t i = 0; i < 4; i++ ) {
uint32_t byte = (value << (i * 8)) >> (24 - i * 8);
hash = ( hash ^ byte ) * 0x01000193U;
}
return hash;
}
void DoubleHashKey::computeHash(uint32_t value, uint32_t primeIndex) {
_hash = DoubleHashKey::hashFunction(value, primeIndex);
_hash2 = DoubleHashKey::hashFunction2(value);
}

View file

@ -0,0 +1,49 @@
//
// DoubleHashKey.h
// libraries/shared/src
//
// Created by Andrew Meadows 2014.11.02
// 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
//
#ifndef hifi_DoubleHashKey_h
#define hifi_DoubleHashKey_h
#include <stdint.h>
// DoubleHashKey for use with btHashMap
class DoubleHashKey {
public:
static uint32_t hashFunction(uint32_t value, uint32_t primeIndex);
static uint32_t hashFunction2(uint32_t value);
DoubleHashKey() : _hash(0), _hash2(0) { }
DoubleHashKey(uint32_t value, uint32_t primeIndex = 0) :
_hash(hashFunction(value, primeIndex)),
_hash2(hashFunction2(value)) {
}
void clear() { _hash = 0; _hash2 = 0; }
bool isNull() const { return _hash == 0 && _hash2 == 0; }
bool equals(const DoubleHashKey& other) const {
return _hash == other._hash && _hash2 == other._hash2;
}
void computeHash(uint32_t value, uint32_t primeIndex = 0);
uint32_t getHash() const { return _hash; }
uint32_t getHash2() const { return _hash2; }
void setHash(uint32_t hash) { _hash = hash; }
void setHash2(uint32_t hash2) { _hash2 = hash2; }
private:
uint32_t _hash;
uint32_t _hash2;
};
#endif // hifi_DoubleHashKey_h

View file

@ -13,54 +13,40 @@
#include "SharedUtil.h" // for MILLIMETERS_PER_METER
//#include "DoubleHashKey.h"
#include "ShapeInfo.h"
void ShapeInfo::clear() {
_type = SHAPE_TYPE_NONE;
_data.clear();
_halfExtents = glm::vec3(0.0f);
_doubleHashKey.clear();
_externalData = NULL;
}
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data) {
}
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
_type = SHAPE_TYPE_BOX;
_data.clear();
// _data[0] = < halfX, halfY, halfZ >
_data.push_back(halfExtents);
_halfExtents = halfExtents;
_doubleHashKey.clear();
}
void ShapeInfo::setSphere(float radius) {
_type = SHAPE_TYPE_SPHERE;
_data.clear();
// _data[0] = < radius, radius, radius >
_data.push_back(glm::vec3(radius));
_halfExtents = glm::vec3(radius, radius, radius);
_doubleHashKey.clear();
}
void ShapeInfo::setCylinder(float radius, float halfHeight) {
_type = SHAPE_TYPE_CYLINDER;
_data.clear();
// _data[0] = < radius, halfHeight, radius >
// NOTE: default cylinder has (UpAxis = 1) axis along yAxis and radius stored in X
_data.push_back(glm::vec3(radius, halfHeight, radius));
void ShapeInfo::setEllipsoid(const glm::vec3& halfExtents) {
_type = SHAPE_TYPE_ELLIPSOID;
_halfExtents = halfExtents;
_doubleHashKey.clear();
}
void ShapeInfo::setCapsule(float radius, float halfHeight) {
_type = SHAPE_TYPE_CAPSULE;
_data.clear();
// _data[0] = < radius, halfHeight, radius >
_data.push_back(glm::vec3(radius, halfHeight, radius));
}
glm::vec3 ShapeInfo::getBoundingBoxDiagonal() const {
switch(_type) {
case SHAPE_TYPE_BOX:
case SHAPE_TYPE_SPHERE:
case SHAPE_TYPE_CYLINDER:
case SHAPE_TYPE_CAPSULE:
return 2.0f * _data[0];
default:
break;
}
return glm::vec3(0.0f);
void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
_type = SHAPE_TYPE_CAPSULE_Y;
_halfExtents = glm::vec3(radius, halfHeight, radius);
_doubleHashKey.clear();
}
float ShapeInfo::computeVolume() const {
@ -68,23 +54,23 @@ float ShapeInfo::computeVolume() const {
float volume = DEFAULT_VOLUME;
switch(_type) {
case SHAPE_TYPE_BOX: {
// factor of 8.0 because the components of _data[0] are all halfExtents
volume = 8.0f * _data[0].x * _data[0].y * _data[0].z;
// 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: {
float radius = _data[0].x;
float radius = _halfExtents.x;
volume = 4.0f * PI * radius * radius * radius / 3.0f;
break;
}
case SHAPE_TYPE_CYLINDER: {
float radius = _data[0].x;
volume = PI * radius * radius * 2.0f * _data[0].y;
case SHAPE_TYPE_CYLINDER_Y: {
float radius = _halfExtents.x;
volume = PI * radius * radius * 2.0f * _halfExtents.y;
break;
}
case SHAPE_TYPE_CAPSULE: {
float radius = _data[0].x;
volume = PI * radius * radius * (2.0f * _data[0].y + 4.0f * radius / 3.0f);
case SHAPE_TYPE_CAPSULE_Y: {
float radius = _halfExtents.x;
volume = PI * radius * radius * (2.0f * _halfExtents.y + 4.0f * radius / 3.0f);
break;
}
default:
@ -93,3 +79,85 @@ float ShapeInfo::computeVolume() const {
assert(volume > 0.0f);
return volume;
}
const DoubleHashKey& ShapeInfo::getHash() const {
// NOTE: we cache the hash so we only ever need to compute it once for any valid ShapeInfo instance.
if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) {
// cast this to non-const pointer so we can do our dirty work
ShapeInfo* thisPtr = const_cast<ShapeInfo*>(this);
// compute hash1
// TODO?: provide lookup table for hash/hash2 of _type rather than recompute?
uint32_t primeIndex = 0;
thisPtr->_doubleHashKey.computeHash((uint32_t)_type, primeIndex++);
const QVector<glm::vec3>* data = getData();
if (data) {
// if externalData exists we use it to continue the hash
// compute hash
uint32_t hash = _doubleHashKey.getHash();
glm::vec3 tmpData;
int numData = data->size();
for (int i = 0; i < numData; ++i) {
tmpData = (*data)[i];
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::hashFunction((uint32_t)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f), primeIndex++);
hash ^= floatHash;
}
}
thisPtr->_doubleHashKey.setHash(hash);
// compute hash2
hash = _doubleHashKey.getHash2();
for (int i = 0; i < numData; ++i) {
tmpData = (*data)[i];
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)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f));
hash += ~(floatHash << 17);
hash ^= (floatHash >> 11);
hash += (floatHash << 4);
hash ^= (floatHash >> 7);
hash += ~(floatHash << 10);
hash = (hash << 16) | (hash >> 16);
}
}
thisPtr->_doubleHashKey.setHash2(hash);
} else {
// this shape info has no external data so type+extents should be enough to generate a unique hash
// 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()
uint32_t floatHash =
DoubleHashKey::hashFunction((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f), primeIndex++);
hash ^= floatHash;
}
thisPtr->_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));
hash += ~(floatHash << 17);
hash ^= (floatHash >> 11);
hash += (floatHash << 4);
hash ^= (floatHash >> 7);
hash += ~(floatHash << 10);
hash = (hash << 16) | (hash >> 16);
}
thisPtr->_doubleHashKey.setHash2(hash);
}
}
return _doubleHashKey;
}

View file

@ -15,40 +15,51 @@
#include <QVector>
#include <glm/glm.hpp>
#include "Shape.h"
#include "DoubleHashKey.h"
enum ShapeType {
SHAPE_TYPE_NONE,
SHAPE_TYPE_BOX,
SHAPE_TYPE_SPHERE,
SHAPE_TYPE_ELLIPSOID,
SHAPE_TYPE_CYLINDER,
SHAPE_TYPE_CAPSULE,
SHAPE_TYPE_CONVEX_HULL,
SHAPE_TYPE_HULL,
SHAPE_TYPE_PLANE,
SHAPE_TYPE_COMPOUND
SHAPE_TYPE_COMPOUND,
SHAPE_TYPE_CAPSULE_X,
SHAPE_TYPE_CAPSULE_Y,
SHAPE_TYPE_CAPSULE_Z,
SHAPE_TYPE_CYLINDER_X,
SHAPE_TYPE_CYLINDER_Y,
SHAPE_TYPE_CYLINDER_Z
};
class ShapeInfo {
public:
ShapeInfo() : _type(SHAPE_TYPE_NONE) {}
void clear();
void setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data = NULL);
void setBox(const glm::vec3& halfExtents);
void setSphere(float radius);
void setCylinder(float radius, float halfHeight);
void setCapsule(float radius, float halfHeight);
void setEllipsoid(const glm::vec3& halfExtents);
//void setHull(); // TODO: implement this
void setCapsuleY(float radius, float halfHeight);
const int getType() const { return _type; }
const QVector<glm::vec3>& getData() const { return _data; }
glm::vec3 getBoundingBoxDiagonal() const;
const glm::vec3& getHalfExtents() const { return _halfExtents; }
void setData(const QVector<glm::vec3>* data) { _externalData = data; }
const QVector<glm::vec3>* getData() const { return _externalData; }
float computeVolume() const;
const DoubleHashKey& getHash() const;
protected:
int _type;
QVector<glm::vec3> _data;
ShapeType _type = SHAPE_TYPE_NONE;
glm::vec3 _halfExtents = glm::vec3(0.0f);
DoubleHashKey _doubleHashKey;
const QVector<glm::vec3>* _externalData = NULL;
};
#endif // hifi_ShapeInfo_h