mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-14 23:07:07 +02:00
217 lines
8.2 KiB
C++
217 lines
8.2 KiB
C++
//
|
|
// CollisionRenderMeshCache.cpp
|
|
// libraries/physics/src
|
|
//
|
|
// Created by Andrew Meadows 2016.07.13
|
|
// Copyright 2016 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 "CollisionRenderMeshCache.h"
|
|
|
|
#include <cassert>
|
|
|
|
#include <btBulletDynamicsCommon.h>
|
|
#include <BulletCollision/CollisionShapes/btShapeHull.h>
|
|
|
|
#include <ShapeInfo.h> // for MAX_HULL_POINTS
|
|
|
|
const int32_t MAX_HULL_INDICES = 6 * MAX_HULL_POINTS;
|
|
const int32_t MAX_HULL_NORMALS = MAX_HULL_INDICES;
|
|
float tempVertices[MAX_HULL_NORMALS];
|
|
model::Index tempIndexBuffer[MAX_HULL_INDICES];
|
|
|
|
bool copyShapeToMesh(const btTransform& transform, const btConvexShape* shape,
|
|
gpu::BufferView& vertices, gpu::BufferView& indices, gpu::BufferView& parts,
|
|
gpu::BufferView& normals) {
|
|
assert(shape);
|
|
|
|
btShapeHull hull(shape);
|
|
if (!hull.buildHull(shape->getMargin())) {
|
|
return false;
|
|
}
|
|
|
|
int32_t numHullIndices = hull.numIndices();
|
|
assert(numHullIndices <= MAX_HULL_INDICES);
|
|
|
|
int32_t numHullVertices = hull.numVertices();
|
|
assert(numHullVertices <= MAX_HULL_POINTS);
|
|
|
|
{ // new part
|
|
model::Mesh::Part part;
|
|
part._startIndex = (model::Index)indices.getNumElements();
|
|
part._numIndices = (model::Index)numHullIndices;
|
|
// FIXME: the render code cannot handle the case where part._baseVertex != 0
|
|
//part._baseVertex = vertices.getNumElements(); // DOES NOT WORK
|
|
part._baseVertex = 0;
|
|
|
|
gpu::BufferView::Size numBytes = sizeof(model::Mesh::Part);
|
|
const gpu::Byte* data = reinterpret_cast<const gpu::Byte*>(&part);
|
|
parts._buffer->append(numBytes, data);
|
|
parts._size = parts._buffer->getSize();
|
|
}
|
|
|
|
const int32_t SIZE_OF_VEC3 = 3 * sizeof(float);
|
|
model::Index indexOffset = (model::Index)vertices.getNumElements();
|
|
|
|
{ // new indices
|
|
const uint32_t* hullIndices = hull.getIndexPointer();
|
|
// FIXME: the render code cannot handle the case where part._baseVertex != 0
|
|
// so we must add an offset to each index
|
|
for (int32_t i = 0; i < numHullIndices; ++i) {
|
|
tempIndexBuffer[i] = hullIndices[i] + indexOffset;
|
|
}
|
|
const gpu::Byte* data = reinterpret_cast<const gpu::Byte*>(tempIndexBuffer);
|
|
gpu::BufferView::Size numBytes = (gpu::BufferView::Size)(sizeof(model::Index) * numHullIndices);
|
|
indices._buffer->append(numBytes, data);
|
|
indices._size = indices._buffer->getSize();
|
|
}
|
|
{ // new vertices
|
|
const btVector3* hullVertices = hull.getVertexPointer();
|
|
assert(numHullVertices <= MAX_HULL_POINTS);
|
|
for (int32_t i = 0; i < numHullVertices; ++i) {
|
|
btVector3 transformedPoint = transform * hullVertices[i];
|
|
memcpy(tempVertices + 3 * i, transformedPoint.m_floats, SIZE_OF_VEC3);
|
|
}
|
|
gpu::BufferView::Size numBytes = sizeof(float) * (3 * numHullVertices);
|
|
const gpu::Byte* data = reinterpret_cast<const gpu::Byte*>(tempVertices);
|
|
vertices._buffer->append(numBytes, data);
|
|
vertices._size = vertices._buffer->getSize();
|
|
}
|
|
{ // new normals
|
|
// compute average point
|
|
btVector3 avgVertex(0.0f, 0.0f, 0.0f);
|
|
const btVector3* hullVertices = hull.getVertexPointer();
|
|
for (int i = 0; i < numHullVertices; ++i) {
|
|
avgVertex += hullVertices[i];
|
|
}
|
|
avgVertex = transform * (avgVertex * (1.0f / (float)numHullVertices));
|
|
|
|
for (int i = 0; i < numHullVertices; ++i) {
|
|
btVector3 norm = transform * hullVertices[i] - avgVertex;
|
|
btScalar normLength = norm.length();
|
|
if (normLength > FLT_EPSILON) {
|
|
norm /= normLength;
|
|
}
|
|
memcpy(tempVertices + 3 * i, norm.m_floats, SIZE_OF_VEC3);
|
|
}
|
|
gpu::BufferView::Size numBytes = sizeof(float) * (3 * numHullVertices);
|
|
const gpu::Byte* data = reinterpret_cast<const gpu::Byte*>(tempVertices);
|
|
normals._buffer->append(numBytes, data);
|
|
normals._size = vertices._buffer->getSize();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
model::MeshPointer createMeshFromShape(const void* pointer) {
|
|
model::MeshPointer mesh;
|
|
if (!pointer) {
|
|
return mesh;
|
|
}
|
|
|
|
// pointer must be a const btCollisionShape* (cast to void*), but it only
|
|
// needs to be valid here when its render mesh is created, after this call
|
|
// the cache doesn't care what happens to the shape behind the pointer
|
|
const btCollisionShape* shape = static_cast<const btCollisionShape*>(pointer);
|
|
|
|
int32_t shapeType = shape->getShapeType();
|
|
if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE || shape->isConvex()) {
|
|
// allocate buffers for it
|
|
gpu::BufferView vertices(new gpu::Buffer(), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
|
|
gpu::BufferView indices(new gpu::Buffer(), gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::INDEX));
|
|
gpu::BufferView parts(new gpu::Buffer(), gpu::Element(gpu::VEC4, gpu::UINT32, gpu::PART));
|
|
gpu::BufferView normals(new gpu::Buffer(), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
|
|
|
|
int32_t numSuccesses = 0;
|
|
if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE) {
|
|
const btCompoundShape* compoundShape = static_cast<const btCompoundShape*>(shape);
|
|
int32_t numSubShapes = compoundShape->getNumChildShapes();
|
|
for (int32_t i = 0; i < numSubShapes; ++i) {
|
|
const btCollisionShape* childShape = compoundShape->getChildShape(i);
|
|
if (childShape->isConvex()) {
|
|
const btConvexShape* convexShape = static_cast<const btConvexShape*>(childShape);
|
|
if (copyShapeToMesh(compoundShape->getChildTransform(i), convexShape, vertices, indices, parts, normals)) {
|
|
numSuccesses++;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// shape is convex
|
|
const btConvexShape* convexShape = static_cast<const btConvexShape*>(shape);
|
|
btTransform transform;
|
|
transform.setIdentity();
|
|
if (copyShapeToMesh(transform, convexShape, vertices, indices, parts, normals)) {
|
|
numSuccesses++;
|
|
}
|
|
}
|
|
if (numSuccesses > 0) {
|
|
mesh = std::make_shared<model::Mesh>();
|
|
mesh->setVertexBuffer(vertices);
|
|
mesh->setIndexBuffer(indices);
|
|
mesh->setPartBuffer(parts);
|
|
mesh->addAttribute(gpu::Stream::NORMAL, normals);
|
|
} else {
|
|
// TODO: log failure message here
|
|
}
|
|
}
|
|
return mesh;
|
|
}
|
|
|
|
CollisionRenderMeshCache::CollisionRenderMeshCache() {
|
|
}
|
|
|
|
CollisionRenderMeshCache::~CollisionRenderMeshCache() {
|
|
_meshMap.clear();
|
|
_pendingGarbage.clear();
|
|
}
|
|
|
|
model::MeshPointer CollisionRenderMeshCache::getMesh(CollisionRenderMeshCache::Key key) {
|
|
model::MeshPointer mesh;
|
|
if (key) {
|
|
CollisionMeshMap::const_iterator itr = _meshMap.find(key);
|
|
if (itr == _meshMap.end()) {
|
|
// make mesh and add it to map
|
|
mesh = createMeshFromShape(key);
|
|
if (mesh) {
|
|
_meshMap.insert(std::make_pair(key, mesh));
|
|
}
|
|
} else {
|
|
mesh = itr->second;
|
|
}
|
|
}
|
|
const uint32_t MAX_NUM_PENDING_GARBAGE = 20;
|
|
if (_pendingGarbage.size() > MAX_NUM_PENDING_GARBAGE) {
|
|
collectGarbage();
|
|
}
|
|
return mesh;
|
|
}
|
|
|
|
bool CollisionRenderMeshCache::releaseMesh(CollisionRenderMeshCache::Key key) {
|
|
if (!key) {
|
|
return false;
|
|
}
|
|
CollisionMeshMap::const_iterator itr = _meshMap.find(key);
|
|
if (itr != _meshMap.end()) {
|
|
_pendingGarbage.push_back(key);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CollisionRenderMeshCache::collectGarbage() {
|
|
uint32_t numShapes = (uint32_t)_pendingGarbage.size();
|
|
for (uint32_t i = 0; i < numShapes; ++i) {
|
|
CollisionRenderMeshCache::Key key = _pendingGarbage[i];
|
|
CollisionMeshMap::const_iterator itr = _meshMap.find(key);
|
|
if (itr != _meshMap.end()) {
|
|
if ((*itr).second.use_count() == 1) {
|
|
// we hold the only reference
|
|
_meshMap.erase(itr);
|
|
}
|
|
}
|
|
}
|
|
_pendingGarbage.clear();
|
|
}
|
|
|