217 lines · 8.2 KB · C++
RawDownload
////  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();}