Merge pull request #11336 from 1P-Cusack/21389_PR2

WL21389 PR2: Representation of collision shapes need updating (details below).
This commit is contained in:
Andrew Meadows 2017-10-25 10:41:21 -07:00 committed by GitHub
commit 2d0b94817c
14 changed files with 425 additions and 153 deletions

View file

@ -120,6 +120,7 @@
#include <SceneScriptingInterface.h>
#include <ScriptEngines.h>
#include <ScriptCache.h>
#include <ShapeEntityItem.h>
#include <SoundCache.h>
#include <ui/TabletScriptingInterface.h>
#include <ui/ToolbarScriptingInterface.h>
@ -4228,6 +4229,10 @@ void Application::init() {
// fire off an immediate domain-server check in now that settings are loaded
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
// This allows collision to be set up properly for shape entities supported by GeometryCache.
// This is before entity setup to ensure that it's ready for whenever instance collision is initialized.
ShapeEntityItem::setShapeInfoCalulator(ShapeEntityItem::ShapeInfoCalculator(&shapeInfoCalculator));
getEntities()->init();
getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) {
auto dims = item.getDimensions();

View file

@ -28,6 +28,8 @@
#include <GeometryCache.h>
#include <OctreeConstants.h>
#include <SharedUtil.h>
#include <ShapeEntityItem.h>
#include <ShapeInfo.h>
#include "InterfaceLogging.h"
#include "world.h"
@ -393,4 +395,20 @@ void runUnitTests() {
}
}
void shapeInfoCalculator(const ShapeEntityItem * const shapeEntity, ShapeInfo &shapeInfo) {
if (shapeEntity == nullptr) {
//--EARLY EXIT--
return;
}
ShapeInfo::PointCollection pointCollection;
ShapeInfo::PointList points;
pointCollection.push_back(points);
GeometryCache::computeSimpleHullPointListForShape((int)shapeEntity->getShape(), shapeEntity->getDimensions(), pointCollection.back());
shapeInfo.setPointCollection(pointCollection);
}

View file

@ -18,6 +18,9 @@
#include <gpu/Batch.h>
#include <render/Forward.h>
class ShapeEntityItem;
class ShapeInfo;
void renderWorldBox(RenderArgs* args, gpu::Batch& batch);
void runTimingTests();
@ -28,4 +31,6 @@ bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNorma
bool pointInSphere(glm::vec3& point, glm::vec3& sphereCenter, double sphereRadius);
void shapeInfoCalculator(const ShapeEntityItem * const shapeEntity, ShapeInfo &shapeInfo);
#endif // hifi_Util_h

View file

@ -30,23 +30,6 @@ using namespace render::entities;
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
static const float SPHERE_ENTITY_SCALE = 0.5f;
static std::array<GeometryCache::Shape, entity::NUM_SHAPES> MAPPING { {
GeometryCache::Triangle,
GeometryCache::Quad,
GeometryCache::Hexagon,
GeometryCache::Octagon,
GeometryCache::Circle,
GeometryCache::Cube,
GeometryCache::Sphere,
GeometryCache::Tetrahedron,
GeometryCache::Octahedron,
GeometryCache::Dodecahedron,
GeometryCache::Icosahedron,
GeometryCache::Torus,
GeometryCache::Cone,
GeometryCache::Cylinder,
} };
ShapeEntityRenderer::ShapeEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {
_procedural._vertexSource = simple_vert;
@ -137,11 +120,12 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
gpu::Batch& batch = *args->_batch;
auto geometryCache = DependencyManager::get<GeometryCache>();
GeometryCache::Shape geometryShape;
bool proceduralRender = false;
glm::vec4 outColor;
withReadLock([&] {
geometryShape = MAPPING[_shape];
geometryShape = geometryCache->getShapeForEntityShape(_shape);
batch.setModelTransform(_renderTransform); // use a transform with scale, rotation, registration point and translation
outColor = _color;
if (_procedural.isReady()) {
@ -155,14 +139,13 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
if (proceduralRender) {
batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a);
if (render::ShapeKey(args->_globalShapeKey).isWireframe()) {
DependencyManager::get<GeometryCache>()->renderWireShape(batch, geometryShape);
geometryCache->renderWireShape(batch, geometryShape);
} else {
DependencyManager::get<GeometryCache>()->renderShape(batch, geometryShape);
geometryCache->renderShape(batch, geometryShape);
}
} else {
// FIXME, support instanced multi-shape rendering using multidraw indirect
outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
auto geometryCache = DependencyManager::get<GeometryCache>();
auto pipeline = outColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
if (render::ShapeKey(args->_globalShapeKey).isWireframe()) {
geometryCache->renderWireShapeInstance(args, batch, geometryShape, outColor, pipeline);
@ -171,6 +154,6 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
}
}
static const auto triCount = DependencyManager::get<GeometryCache>()->getShapeTriangleCount(geometryShape);
const auto triCount = geometryCache->getShapeTriangleCount(geometryShape);
args->_details._trianglesRendered += (int)triCount;
}

View file

@ -84,28 +84,11 @@ void EntityItemProperties::setLastEdited(quint64 usecTime) {
_lastEdited = usecTime > _created ? usecTime : _created;
}
const char* shapeTypeNames[] = {
"none",
"box",
"sphere",
"capsule-x",
"capsule-y",
"capsule-z",
"cylinder-x",
"cylinder-y",
"cylinder-z",
"hull",
"plane",
"compound",
"simple-hull",
"simple-compound",
"static-mesh"
};
QHash<QString, ShapeType> stringToShapeTypeLookup;
void addShapeType(ShapeType type) {
stringToShapeTypeLookup[shapeTypeNames[type]] = type;
stringToShapeTypeLookup[ShapeInfo::getNameForShapeType(type)] = type;
}
void buildStringToShapeTypeLookup() {
@ -180,9 +163,7 @@ void EntityItemProperties::setCollisionMaskFromString(const QString& maskString)
}
QString EntityItemProperties::getShapeTypeAsString() const {
if (_shapeType < sizeof(shapeTypeNames) / sizeof(char *))
return QString(shapeTypeNames[_shapeType]);
return QString(shapeTypeNames[SHAPE_TYPE_NONE]);
return ShapeInfo::getNameForShapeType(_shapeType);
}
void EntityItemProperties::setShapeTypeFromString(const QString& shapeName) {

View file

@ -51,6 +51,14 @@ namespace entity {
}
}
// shapeCalculator is a hook for external code that knows how to configure a ShapeInfo
// for given entity::Shape and dimensions
ShapeEntityItem::ShapeInfoCalculator shapeCalculator = nullptr;
void ShapeEntityItem::setShapeInfoCalulator(ShapeEntityItem::ShapeInfoCalculator callback) {
shapeCalculator = callback;
}
ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
Pointer entity(new ShapeEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); });
entity->setProperties(properties);
@ -87,6 +95,7 @@ EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredP
}
void ShapeEntityItem::setShape(const entity::Shape& shape) {
const entity::Shape prevShape = _shape;
_shape = shape;
switch (_shape) {
case entity::Shape::Cube:
@ -99,6 +108,11 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) {
_type = EntityTypes::Shape;
break;
}
if (_shape != prevShape) {
// Internally grabs writeLock
markDirtyFlags(Simulation::DIRTY_SHAPE);
}
}
bool ShapeEntityItem::setProperties(const EntityItemProperties& properties) {
@ -219,6 +233,7 @@ void ShapeEntityItem::debugDump() const {
qCDebug(entities) << "SHAPE EntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " name:" << _name;
qCDebug(entities) << " shape:" << stringFromShape(_shape) << " (EnumId: " << _shape << " )";
qCDebug(entities) << " collisionShapeType:" << ShapeInfo::getNameForShapeType(getShapeType());
qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2];
qCDebug(entities) << " position:" << debugTreeVector(getPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
@ -233,73 +248,101 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
const glm::vec3 entityDimensions = getDimensions();
switch (_shape) {
case entity::Shape::Quad:
case entity::Shape::Cube:
{
_collisionShapeType = SHAPE_TYPE_BOX;
switch (_shape){
case entity::Shape::Quad: {
// Not in GeometryCache::buildShapes, unsupported.
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
//TODO WL21389: Add a SHAPE_TYPE_QUAD ShapeType and treat
// as a special box (later if desired support)
}
break;
case entity::Shape::Cube: {
_collisionShapeType = SHAPE_TYPE_BOX;
}
break;
case entity::Shape::Sphere: {
float diameter = entityDimensions.x;
const float MIN_DIAMETER = 0.001f;
const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f;
if (diameter > MIN_DIAMETER
&& fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR
&& fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) {
_collisionShapeType = SHAPE_TYPE_SPHERE;
} else {
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
}
break;
case entity::Shape::Sphere:
{
}
break;
case entity::Shape::Circle: {
_collisionShapeType = SHAPE_TYPE_CIRCLE;
}
break;
case entity::Shape::Cylinder: {
_collisionShapeType = SHAPE_TYPE_CYLINDER_Y;
// TODO WL21389: determine if rotation is axis-aligned
//const Transform::Quat & rot = _transform.getRotation();
float diameter = entityDimensions.x;
const float MIN_DIAMETER = 0.001f;
const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f;
if (diameter > MIN_DIAMETER
&& fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR
&& fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) {
// TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and
// hull ( or dimensions, need circular cross section)
// Should allow for minor variance along axes?
_collisionShapeType = SHAPE_TYPE_SPHERE;
} else {
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
}
}
break;
case entity::Shape::Cone: {
if (shapeCalculator) {
shapeCalculator(this, info);
// shapeCalculator only supports convex shapes (e.g. SHAPE_TYPE_HULL)
_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;
} else {
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
}
break;
case entity::Shape::Cylinder:
{
_collisionShapeType = SHAPE_TYPE_CYLINDER_Y;
// TODO WL21389: determine if rotation is axis-aligned
//const Transform::Quat & rot = _transform.getRotation();
// TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and
// hull ( or dimensions, need circular cross section)
// Should allow for minor variance along axes?
}
break;
}
break;
// gons, ones, & angles built via GeometryCache::extrudePolygon
case entity::Shape::Triangle:
case entity::Shape::Hexagon:
case entity::Shape::Octagon:
case entity::Shape::Circle:
case entity::Shape::Octagon: {
if (shapeCalculator) {
shapeCalculator(this, info);
// shapeCalculator only supports convex shapes (e.g. SHAPE_TYPE_HULL)
_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;
} else {
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
}
}
break;
// hedrons built via GeometryCache::setUpFlatShapes
case entity::Shape::Tetrahedron:
case entity::Shape::Octahedron:
case entity::Shape::Dodecahedron:
case entity::Shape::Icosahedron:
case entity::Shape::Cone:
{
//TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later)
case entity::Shape::Icosahedron: {
if ( shapeCalculator ) {
shapeCalculator(this, info);
// shapeCalculator only supports convex shapes (e.g. SHAPE_TYPE_HULL)
_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;
} else {
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
}
break;
case entity::Shape::Torus:
{
// Not in GeometryCache::buildShapes, unsupported.
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
//TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support)
}
break;
default:
{
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
}
break;
}
break;
case entity::Shape::Torus: {
// Not in GeometryCache::buildShapes, unsupported.
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
//TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support)
}
break;
default: {
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
}
break;
}
EntityItem::computeShapeInfo(info);
}
// This value specifes how the shape should be treated by physics calculations.
// This value specifies how the shape should be treated by physics calculations.
ShapeType ShapeEntityItem::getShapeType() const {
return _collisionShapeType;
}

View file

@ -34,7 +34,6 @@ namespace entity {
::QString stringFromShape(Shape shape);
}
class ShapeEntityItem : public EntityItem {
using Pointer = std::shared_ptr<ShapeEntityItem>;
static Pointer baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
@ -43,6 +42,9 @@ public:
static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
using ShapeInfoCalculator = std::function<void( const ShapeEntityItem * const shapeEntity, ShapeInfo& info)>;
static void setShapeInfoCalulator(ShapeInfoCalculator callback);
ShapeEntityItem(const EntityItemID& entityItemID);
void pureVirtualFunctionPlaceHolder() override { };

View file

@ -66,7 +66,7 @@ class PhysicsEngine;
class ObjectMotionState : public btMotionState {
public:
// These poroperties of the PhysicsEngine are "global" within the context of all ObjectMotionStates
// These properties of the PhysicsEngine are "global" within the context of all ObjectMotionStates
// (assuming just one PhysicsEngine). They are cached as statics for fast calculations in the
// ObjectMotionState context.
static void setWorldOffset(const glm::vec3& offset);

View file

@ -314,6 +314,7 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info)
shape = new btCylinderShapeZ(btHalfExtents);
}
break;
case SHAPE_TYPE_CIRCLE:
case SHAPE_TYPE_CYLINDER_Y: {
const glm::vec3 halfExtents = info.getHalfExtents();
const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z);

View file

@ -52,6 +52,46 @@
//#define WANT_DEBUG
// @note: Originally size entity::NUM_SHAPES
// As of Commit b93e91b9, render-utils no longer retains knowledge of
// entity lib, and thus doesn't know about entity::NUM_SHAPES. Should
// the enumerations be altered, this will need to be updated.
// @see ShapeEntityItem.h
static std::array<GeometryCache::Shape, (GeometryCache::NUM_SHAPES - 1)> MAPPING{ {
GeometryCache::Triangle,
GeometryCache::Quad,
GeometryCache::Hexagon,
GeometryCache::Octagon,
GeometryCache::Circle,
GeometryCache::Cube,
GeometryCache::Sphere,
GeometryCache::Tetrahedron,
GeometryCache::Octahedron,
GeometryCache::Dodecahedron,
GeometryCache::Icosahedron,
GeometryCache::Torus,
GeometryCache::Cone,
GeometryCache::Cylinder,
} };
static const std::array<const char * const, GeometryCache::NUM_SHAPES> GEOCACHE_SHAPE_STRINGS{ {
"Line",
"Triangle",
"Quad",
"Hexagon",
"Octagon",
"Circle",
"Cube",
"Sphere",
"Tetrahedron",
"Octahedron",
"Dodecahedron",
"Icosahedron",
"Torus",
"Cone",
"Cylinder"
} };
const int GeometryCache::UNKNOWN_ID = -1;
@ -69,6 +109,51 @@ static gpu::Stream::FormatPointer INSTANCED_SOLID_FADE_STREAM_FORMAT;
static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec3) * 2; // vertices and normals
static const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3);
void GeometryCache::computeSimpleHullPointListForShape(const int entityShape, const glm::vec3 &entityExtents, QVector<glm::vec3> &outPointList) {
auto geometryCache = DependencyManager::get<GeometryCache>();
const GeometryCache::Shape geometryShape = GeometryCache::getShapeForEntityShape( entityShape );
const GeometryCache::ShapeData * shapeData = geometryCache->getShapeData( geometryShape );
if (!shapeData){
//--EARLY EXIT--( data isn't ready for some reason... )
return;
}
const gpu::BufferView & shapeVerts = shapeData->_positionView;
const gpu::BufferView::Size numItems = shapeVerts.getNumElements();
outPointList.reserve((int)numItems);
QVector<glm::vec3> uniqueVerts;
uniqueVerts.reserve((int)numItems);
const float MAX_INCLUSIVE_FILTER_DISTANCE_SQUARED = 1.0e-6f; //< 1mm^2
for (gpu::BufferView::Index i = 0; i < (gpu::BufferView::Index)numItems; ++i) {
const int numUniquePoints = (int)uniqueVerts.size();
const geometry::Vec &curVert = shapeVerts.get<geometry::Vec>(i);
bool isUniquePoint = true;
for (int uniqueIndex = 0; uniqueIndex < numUniquePoints; ++uniqueIndex) {
const geometry::Vec knownVert = uniqueVerts[uniqueIndex];
const float distToKnownPoint = glm::length2(knownVert - curVert);
if (distToKnownPoint <= MAX_INCLUSIVE_FILTER_DISTANCE_SQUARED) {
isUniquePoint = false;
break;
}
}
if (!isUniquePoint) {
//--EARLY ITERATION EXIT--
continue;
}
uniqueVerts.push_back(curVert);
outPointList.push_back(curVert * entityExtents);
}
}
template <size_t SIDES>
std::vector<vec3> polygon() {
std::vector<vec3> result;
@ -85,7 +170,7 @@ void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, c
gpu::Buffer::Size offset = vertexBuffer->getSize();
vertexBuffer->append(vertices);
gpu::Buffer::Size viewSize = vertices.size() * 2 * sizeof(glm::vec3);
gpu::Buffer::Size viewSize = vertices.size() * sizeof(glm::vec3);
_positionView = gpu::BufferView(vertexBuffer, offset,
viewSize, SHAPE_VERTEX_STRIDE, POSITION_ELEMENT);
@ -441,12 +526,46 @@ void GeometryCache::buildShapes() {
extrudePolygon<64>(_shapes[Cone], _shapeVertices, _shapeIndices, true);
//Circle
drawCircle(_shapes[Circle], _shapeVertices, _shapeIndices);
// Not implememented yet:
// Not implemented yet:
//Quad,
//Torus,
}
const GeometryCache::ShapeData * GeometryCache::getShapeData(const Shape shape) const {
if (((int)shape < 0) || ((int)shape >= (int)_shapes.size())) {
qCWarning(renderutils) << "GeometryCache::getShapeData - Invalid shape " << shape << " specified. Returning default fallback.";
//--EARLY EXIT--( No valid shape data for shape )
return nullptr;
}
return &_shapes[shape];
}
GeometryCache::Shape GeometryCache::getShapeForEntityShape(int entityShape) {
if ((entityShape < 0) || (entityShape >= (int)MAPPING.size())) {
qCWarning(renderutils) << "GeometryCache::getShapeForEntityShape - Invalid shape " << entityShape << " specified. Returning default fallback.";
//--EARLY EXIT--( fall back to default assumption )
return GeometryCache::Sphere;
}
return MAPPING[entityShape];
}
QString GeometryCache::stringFromShape(GeometryCache::Shape geoShape)
{
if (((int)geoShape < 0) || ((int)geoShape >= (int)GeometryCache::NUM_SHAPES)) {
qCWarning(renderutils) << "GeometryCache::stringFromShape - Invalid shape " << geoShape << " specified.";
//--EARLY EXIT--
return "INVALID_GEOCACHE_SHAPE";
}
return GEOCACHE_SHAPE_STRINGS[geoShape];
}
gpu::Stream::FormatPointer& getSolidStreamFormat() {
if (!SOLID_STREAM_FORMAT) {
SOLID_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone

View file

@ -147,6 +147,16 @@ public:
NUM_SHAPES,
};
/// @param entityShapeEnum: The entity::Shape enumeration for the shape
/// whose GeometryCache::Shape is desired.
/// @return GeometryCache::NUM_SHAPES in the event of an error; otherwise,
/// the GeometryCache::Shape enum which aligns with the
/// specified entityShapeEnum
static GeometryCache::Shape getShapeForEntityShape(int entityShapeEnum);
static QString stringFromShape(GeometryCache::Shape geoShape);
static void computeSimpleHullPointListForShape(int entityShape, const glm::vec3 &entityExtents, QVector<glm::vec3> &outPointList);
static uint8_t CUSTOM_PIPELINE_NUMBER;
static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key);
static void registerShapePipeline() {
@ -355,15 +365,21 @@ public:
using VShape = std::array<ShapeData, NUM_SHAPES>;
VShape _shapes;
/// returns ShapeData associated with the specified shape,
/// otherwise nullptr in the event of an error.
const ShapeData * getShapeData(Shape shape) const;
private:
GeometryCache();
virtual ~GeometryCache();
void buildShapes();
typedef QPair<int, int> IntPair;
typedef QPair<unsigned int, unsigned int> VerticesIndices;
VShape _shapes;
gpu::PipelinePointer _standardDrawPipeline;
gpu::PipelinePointer _standardDrawPipelineNoBlend;

View file

@ -15,9 +15,38 @@
#include "NumericalConstants.h" // for MILLIMETERS_PER_METER
// Originally within EntityItemProperties.cpp
const char* shapeTypeNames[] = {
"none",
"box",
"sphere",
"capsule-x",
"capsule-y",
"capsule-z",
"cylinder-x",
"cylinder-y",
"cylinder-z",
"hull",
"plane",
"compound",
"simple-hull",
"simple-compound",
"static-mesh"
};
static const size_t SHAPETYPE_NAME_COUNT = (sizeof(shapeTypeNames) / sizeof((shapeTypeNames)[0]));
// Bullet doesn't support arbitrarily small shapes
const float MIN_HALF_EXTENT = 0.005f; // 0.5 cm
QString ShapeInfo::getNameForShapeType(ShapeType type) {
if (((int)type <= 0) || ((int)type >= (int)SHAPETYPE_NAME_COUNT)) {
type = (ShapeType)0;
}
return shapeTypeNames[(int)type];
}
void ShapeInfo::clear() {
_url.clear();
_pointCollection.clear();
@ -29,7 +58,6 @@ void ShapeInfo::clear() {
}
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) {
//TODO WL21389: Does this need additional cases and handling added?
_url = "";
_type = type;
setHalfExtents(halfExtents);
@ -38,6 +66,8 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
_halfExtents = glm::vec3(0.0f);
break;
case SHAPE_TYPE_BOX:
case SHAPE_TYPE_HULL:
case SHAPE_TYPE_SIMPLE_HULL:
break;
case SHAPE_TYPE_SPHERE: {
float radius = glm::length(halfExtents) / SQUARE_ROOT_OF_3;
@ -45,6 +75,10 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
_halfExtents = glm::vec3(radius);
}
break;
case SHAPE_TYPE_CIRCLE: {
_halfExtents = glm::vec3(_halfExtents.x, MIN_HALF_EXTENT, _halfExtents.z);
}
break;
case SHAPE_TYPE_COMPOUND:
case SHAPE_TYPE_SIMPLE_HULL:
case SHAPE_TYPE_SIMPLE_COMPOUND:
@ -58,9 +92,6 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
}
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
//TODO WL21389: Should this pointlist clearance added in case
// this is a re-purposed instance?
// See https://github.com/highfidelity/hifi/pull/11024#discussion_r128885491
_url = "";
_type = SHAPE_TYPE_BOX;
setHalfExtents(halfExtents);
@ -68,7 +99,6 @@ void ShapeInfo::setBox(const glm::vec3& halfExtents) {
}
void ShapeInfo::setSphere(float radius) {
//TODO WL21389: See comment in setBox regarding clearance
_url = "";
_type = SHAPE_TYPE_SPHERE;
radius = glm::max(radius, MIN_HALF_EXTENT);
@ -77,14 +107,11 @@ void ShapeInfo::setSphere(float radius) {
}
void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) {
//TODO WL21389: May need to skip resetting type here.
_pointCollection = pointCollection;
_type = (_pointCollection.size() > 0) ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE;
_doubleHashKey.clear();
}
void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
//TODO WL21389: See comment in setBox regarding clearance
_url = "";
_type = SHAPE_TYPE_CAPSULE_Y;
radius = glm::max(radius, MIN_HALF_EXTENT);
@ -125,8 +152,15 @@ int ShapeInfo::getLargestSubshapePointCount() const {
return numPoints;
}
float computeCylinderVolume(const float radius, const float height) {
return PI * radius * radius * 2.0f * height;
}
float computeCapsuleVolume(const float radius, const float cylinderHeight) {
return PI * radius * radius * (cylinderHeight + 4.0f * radius / 3.0f);
}
float ShapeInfo::computeVolume() const {
//TODO WL21389: Add support for other ShapeTypes( CYLINDER_X, CYLINDER_Y, etc).
const float DEFAULT_VOLUME = 1.0f;
float volume = DEFAULT_VOLUME;
switch(_type) {
@ -139,17 +173,37 @@ float ShapeInfo::computeVolume() const {
volume = 4.0f * PI * _halfExtents.x * _halfExtents.y * _halfExtents.z / 3.0f;
break;
}
case SHAPE_TYPE_CYLINDER_X: {
volume = computeCylinderVolume(_halfExtents.y, _halfExtents.x);
break;
}
case SHAPE_TYPE_CYLINDER_Y: {
float radius = _halfExtents.x;
volume = PI * radius * radius * 2.0f * _halfExtents.y;
volume = computeCylinderVolume(_halfExtents.x, _halfExtents.y);
break;
}
case SHAPE_TYPE_CYLINDER_Z: {
volume = computeCylinderVolume(_halfExtents.x, _halfExtents.z);
break;
}
case SHAPE_TYPE_CAPSULE_X: {
// Need to offset halfExtents.x by y to account for the system treating
// the x extent of the capsule as the cylindrical height + spherical radius.
const float cylinderHeight = 2.0f * (_halfExtents.x - _halfExtents.y);
volume = computeCapsuleVolume(_halfExtents.y, cylinderHeight);
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);
const float cylinderHeight = 2.0f * (_halfExtents.y - _halfExtents.x);
volume = computeCapsuleVolume(_halfExtents.x, cylinderHeight);
break;
}
case SHAPE_TYPE_CAPSULE_Z: {
// Need to offset halfExtents.z by x to account for the system treating
// the z extent of the capsule as the cylindrical height + spherical radius.
const float cylinderHeight = 2.0f * (_halfExtents.z - _halfExtents.x);
volume = computeCapsuleVolume(_halfExtents.x, cylinderHeight);
break;
}
default:
@ -160,7 +214,6 @@ float ShapeInfo::computeVolume() const {
}
bool ShapeInfo::contains(const glm::vec3& point) const {
//TODO WL21389: Add support for other ShapeTypes like Ellipsoid/Compound.
switch(_type) {
case SHAPE_TYPE_SPHERE:
return glm::length(point) <= _halfExtents.x;
@ -205,7 +258,6 @@ bool ShapeInfo::contains(const glm::vec3& point) const {
}
const DoubleHashKey& ShapeInfo::getHash() const {
//TODO WL21389: Need to include the pointlist for SIMPLE_HULL in hash
// 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;
@ -216,41 +268,82 @@ const DoubleHashKey& ShapeInfo::getHash() const {
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(
if (_type != SHAPE_TYPE_SIMPLE_HULL) {
// 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(
if (useOffset) {
hash ^= DoubleHashKey::hashFunction(
(uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f),
primeIndex++);
}
}
}
_doubleHashKey.setHash(hash);
_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(
// 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(
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);
}
hash += ~(floatHash << 17);
hash ^= (floatHash >> 11);
hash += (floatHash << 4);
hash ^= (floatHash >> 7);
hash += ~(floatHash << 10);
hash = (hash << 16) | (hash >> 16);
_doubleHashKey.setHash2(hash);
} else {
assert(_pointCollection.size() == (size_t)1);
const PointList & points = _pointCollection.back();
const int numPoints = (int)points.size();
uint32_t hash = _doubleHashKey.getHash();
uint32_t hash2 = _doubleHashKey.getHash2();
for (int pointIndex = 0; pointIndex < numPoints; ++pointIndex) {
// compute hash1 & 2
const glm::vec3 &curPoint = points[pointIndex];
for (int vecCompIndex = 0; vecCompIndex < 3; ++vecCompIndex) {
// 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 valueToHash = (uint32_t)(curPoint[vecCompIndex] * MILLIMETERS_PER_METER + copysignf(1.0f, curPoint[vecCompIndex]) * 0.49f);
hash ^= DoubleHashKey::hashFunction(valueToHash, primeIndex++);
uint32_t floatHash = DoubleHashKey::hashFunction2(valueToHash);
if (useOffset) {
const uint32_t offsetValToHash = (uint32_t)(_offset[vecCompIndex] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[vecCompIndex])* 0.49f);
hash ^= DoubleHashKey::hashFunction(offsetValToHash, primeIndex++);
floatHash ^= DoubleHashKey::hashFunction2(offsetValToHash);
}
hash2 += ~(floatHash << 17);
hash2 ^= (floatHash >> 11);
hash2 += (floatHash << 4);
hash2 ^= (floatHash >> 7);
hash2 += ~(floatHash << 10);
hash2 = (hash2 << 16) | (hash2 >> 16);
}
}
_doubleHashKey.setHash(hash);
_doubleHashKey.setHash2(hash2);
}
_doubleHashKey.setHash2(hash);
QString url = _url.toString();
if (!url.isEmpty()) {

View file

@ -46,7 +46,8 @@ enum ShapeType {
SHAPE_TYPE_SIMPLE_HULL,
SHAPE_TYPE_SIMPLE_COMPOUND,
SHAPE_TYPE_STATIC_MESH,
SHAPE_TYPE_ELLIPSOID
SHAPE_TYPE_ELLIPSOID,
SHAPE_TYPE_CIRCLE
};
class ShapeInfo {
@ -57,6 +58,8 @@ public:
using PointCollection = QVector<PointList>;
using TriangleIndices = QVector<int32_t>;
static QString getNameForShapeType(ShapeType type);
void clear();
void setParams(ShapeType type, const glm::vec3& halfExtents, QString url="");
@ -66,7 +69,7 @@ public:
void setCapsuleY(float radius, float halfHeight);
void setOffset(const glm::vec3& offset);
int getType() const { return _type; }
ShapeType getType() const { return _type; }
const glm::vec3& getHalfExtents() const { return _halfExtents; }
const glm::vec3& getOffset() const { return _offset; }

View file

@ -10,18 +10,18 @@
gpu::Stream::FormatPointer& getInstancedSolidStreamFormat();
static const size_t TYPE_COUNT = 4;
static const size_t ITEM_COUNT = 50;
static const float SHAPE_INTERVAL = (PI * 2.0f) / ITEM_COUNT;
static const float ITEM_INTERVAL = SHAPE_INTERVAL / TYPE_COUNT;
static GeometryCache::Shape SHAPE[TYPE_COUNT] = {
static GeometryCache::Shape SHAPE[] = {
GeometryCache::Icosahedron,
GeometryCache::Cube,
GeometryCache::Sphere,
GeometryCache::Tetrahedron,
};
static const size_t TYPE_COUNT = (sizeof(SHAPE) / sizeof((SHAPE)[0]));
static const size_t ITEM_COUNT = 50;
static const float SHAPE_INTERVAL = (PI * 2.0f) / ITEM_COUNT;
static const float ITEM_INTERVAL = SHAPE_INTERVAL / TYPE_COUNT;
const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA };
@ -34,8 +34,6 @@ TestInstancedShapes::TestInstancedShapes() {
static const float ITEM_RADIUS = 20;
static const vec3 ITEM_TRANSLATION { 0, 0, -ITEM_RADIUS };
for (size_t i = 0; i < TYPE_COUNT; ++i) {
GeometryCache::Shape shape = SHAPE[i];
GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape];
//indirectCommand._count
float startingInterval = ITEM_INTERVAL * i;
std::vector<mat4> typeTransforms;
@ -62,7 +60,12 @@ void TestInstancedShapes::renderTest(size_t testId, RenderArgs* args) {
batch.setInputFormat(getInstancedSolidStreamFormat());
for (size_t i = 0; i < TYPE_COUNT; ++i) {
GeometryCache::Shape shape = SHAPE[i];
GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape];
const GeometryCache::ShapeData *shapeData = geometryCache->getShapeData( shape );
if (!shapeData) {
//--EARLY ITERATION EXIT--( didn't have shape data yet )
continue;
}
std::string namedCall = __FUNCTION__ + std::to_string(i);
@ -71,13 +74,13 @@ void TestInstancedShapes::renderTest(size_t testId, RenderArgs* args) {
batch.setModelTransform(transforms[i][j]);
batch.setupNamedCalls(namedCall, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData&) {
batch.setInputBuffer(gpu::Stream::COLOR, gpu::BufferView(colorBuffer, i * ITEM_COUNT * 4, colorBuffer->getSize(), COLOR_ELEMENT));
shapeData.drawInstances(batch, ITEM_COUNT);
shapeData->drawInstances(batch, ITEM_COUNT);
});
}
//for (size_t j = 0; j < ITEM_COUNT; ++j) {
// batch.setModelTransform(transforms[j + i * ITEM_COUNT]);
// shapeData.draw(batch);
// shapeData->draw(batch);
//}
}
}