diff --git a/libraries/gpu/src/gpu/Stream.h b/libraries/gpu/src/gpu/Stream.h index d47a877669..8c0833dd42 100644 --- a/libraries/gpu/src/gpu/Stream.h +++ b/libraries/gpu/src/gpu/Stream.h @@ -106,6 +106,7 @@ public: bool setAttribute(Slot slot, Frequency frequency = PER_VERTEX); bool setAttribute(Slot slot, Slot channel, Frequency frequency = PER_VERTEX); + bool hasAttribute(Slot slot) const { return (_attributes.find(slot) != _attributes.end()); } protected: AttributeMap _attributes; diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index 63adeec0f1..df86ba2a8f 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -92,7 +92,7 @@ void Mesh::setPartBuffer(const BufferView& buffer) { _partBuffer = buffer; } -const Box Mesh::evalPartBound(int partNum) const { +Box Mesh::evalPartBound(int partNum) const { Box box; if (partNum < _partBuffer.getNum()) { const Part& part = _partBuffer.get(partNum); @@ -111,7 +111,7 @@ const Box Mesh::evalPartBound(int partNum) const { return box; } -const Box Mesh::evalPartBounds(int partStart, int partEnd, Boxes& bounds) const { +Box Mesh::evalPartBounds(int partStart, int partEnd, Boxes& bounds) const { Box totalBound; auto part = _partBuffer.cbegin() + partStart; auto partItEnd = _partBuffer.cbegin() + partEnd; diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index 8b5f448a64..a35d55d938 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -107,10 +107,10 @@ public: uint getNumParts() const { return _partBuffer.getNumElements(); } // evaluate the bounding box of A part - const Box evalPartBound(int partNum) const; + Box evalPartBound(int partNum) const; // evaluate the bounding boxes of the parts in the range [start, end[ and fill the bounds parameter // the returned box is the bounding box of ALL the evaluated part bounds. - const Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const; + Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const; static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast(topo); } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index bb37bb4932..2d8d7b598b 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -25,7 +25,7 @@ namespace render { // Return opaque for lack of a better idea return ItemKey::Builder::opaqueShape(); } - + template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { if (payload) { return payload->getBound(); @@ -39,55 +39,40 @@ namespace render { using namespace render; -MeshPartPayload::MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, - glm::vec3 position, glm::quat orientation) : - model(model), - meshIndex(meshIndex), - partIndex(partIndex), - _shapeID(shapeIndex), - _modelPosition(position), - _modelOrientation(orientation) { - initCache(); +MeshPartPayload::MeshPartPayload(model::MeshPointer mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform) { + + updateMeshPart(mesh, partIndex); + updateMaterial(material); + updateTransform(transform, offsetTransform); } -void MeshPartPayload::initCache() { - const std::vector>& networkMeshes = model->_geometry->getMeshes(); - const NetworkMesh& networkMesh = *(networkMeshes.at(meshIndex).get()); - _drawMesh = networkMesh._mesh; - - const FBXGeometry& geometry = model->_geometry->getFBXGeometry(); - const FBXMesh& mesh = geometry.meshes.at(meshIndex); - _hasColorAttrib = !mesh.colors.isEmpty(); - _isBlendShaped = !mesh.blendshapes.isEmpty(); - _isSkinned = !mesh.clusterIndices.isEmpty(); - - - _drawPart = _drawMesh->getPartBuffer().get(partIndex); - - auto networkMaterial = model->_geometry->getShapeMaterial(_shapeID); - if (networkMaterial) { - _drawMaterial = networkMaterial->_material; - }; +void MeshPartPayload::updateMeshPart(model::MeshPointer drawMesh, int partIndex) { + _drawMesh = drawMesh; + if (_drawMesh) { + auto vertexFormat = _drawMesh->getVertexFormat(); + _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); + _drawPart = _drawMesh->getPartBuffer().get(partIndex); + _localBound = _drawMesh->evalPartBound(partIndex); + } } -void MeshPartPayload::updateModelLocation(glm::vec3 position, glm::quat orientation) { - _modelPosition = position; - _modelOrientation = orientation; +void MeshPartPayload::updateTransform(const Transform& transform, const Transform& offsetTransform) { + _transform = transform; + _offsetTransform = offsetTransform; + Transform::mult(_drawTransform, _transform, _offsetTransform); + _worldBound = _localBound; + _worldBound.transform(_drawTransform); +} + +void MeshPartPayload::updateMaterial(model::MaterialPointer drawMaterial) { + _drawMaterial = drawMaterial; } render::ItemKey MeshPartPayload::getKey() const { ItemKey::Builder builder; builder.withTypeShape(); - if (!model->isVisible()) { - builder.withInvisible(); - } - - if (_isBlendShaped || _isSkinned) { - builder.withDeformed(); - } - if (_drawMaterial) { auto matKey = _drawMaterial->getKey(); if (matKey.isTransparent() || matKey.isTransparentMap()) { @@ -99,9 +84,7 @@ render::ItemKey MeshPartPayload::getKey() const { } render::Item::Bound MeshPartPayload::getBound() const { - // NOTE: we can't cache this bounds because we need to handle the case of a moving - // entity or mesh part. - return model->getPartBounds(meshIndex, partIndex, _modelPosition, _modelOrientation); + return _worldBound; } void MeshPartPayload::drawCall(gpu::Batch& batch) const { @@ -109,22 +92,12 @@ void MeshPartPayload::drawCall(gpu::Batch& batch) const { } void MeshPartPayload::bindMesh(gpu::Batch& batch) const { - if (!_isBlendShaped) { - batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); - - batch.setInputFormat((_drawMesh->getVertexFormat())); - - batch.setInputStream(0, _drawMesh->getVertexStream()); - } else { - batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); + batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); - batch.setInputFormat((_drawMesh->getVertexFormat())); + batch.setInputFormat((_drawMesh->getVertexFormat())); + + batch.setInputStream(0, _drawMesh->getVertexStream()); - batch.setInputBuffer(0, model->_blendedVertexBuffers[meshIndex], 0, sizeof(glm::vec3)); - batch.setInputBuffer(1, model->_blendedVertexBuffers[meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); - batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); - } - // TODO: Get rid of that extra call if (!_hasColorAttrib) { batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); @@ -215,31 +188,208 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ModelRender::Locatio } void MeshPartPayload::bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const { - // Still relying on the raw data from the model - const Model::MeshState& state = model->_meshStates.at(meshIndex); - - Transform transform; - if (state.clusterBuffer) { - if (model->_cauterizeBones) { - batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.cauterizedClusterBuffer); - } else { - batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.clusterBuffer); - } - } else { - if (model->_cauterizeBones) { - transform = Transform(state.cauterizedClusterMatrices[0]); - } else { - transform = Transform(state.clusterMatrices[0]); - } - } - transform.preTranslate(_modelPosition); - batch.setModelTransform(transform); + batch.setModelTransform(_drawTransform); } void MeshPartPayload::render(RenderArgs* args) const { PerformanceTimer perfTimer("MeshPartPayload::render"); - if (!model->_readyWhenAdded || !model->_isVisible) { + + + gpu::Batch& batch = *(args->_batch); + auto mode = args->_renderMode; + + auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME + + model::MaterialKey drawMaterialKey; + if (_drawMaterial) { + drawMaterialKey = _drawMaterial->getKey(); + } + bool translucentMesh = drawMaterialKey.isTransparent() || drawMaterialKey.isTransparentMap(); + + bool hasTangents = drawMaterialKey.isNormalMap(); + bool hasSpecular = drawMaterialKey.isGlossMap(); + bool hasLightmap = drawMaterialKey.isLightmapMap(); + bool isSkinned = false; + bool wireframe = false; + if (wireframe) { + translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; + } + + ModelRender::Locations* locations = nullptr; + ModelRender::pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, + args, locations); + + + // Bind the model transform and the skinCLusterMatrices if needed + bindTransform(batch, locations); + + //Bind the index buffer and vertex buffer and Blend shapes if needed + bindMesh(batch); + + // apply material properties + bindMaterial(batch, locations); + + + // TODO: We should be able to do that just in the renderTransparentJob + if (translucentMesh && locations->lightBufferUnit >= 0) { + PerformanceTimer perfTimer("DLE->setupTransparent()"); + + DependencyManager::get()->setupTransparent(args, locations->lightBufferUnit); + } + if (args) { + args->_details._materialSwitches++; + } + + // Draw! + { + PerformanceTimer perfTimer("batch.drawIndexed()"); + drawCall(batch); + } + + if (args) { + const int INDICES_PER_TRIANGLE = 3; + args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; + } +} + + + +namespace render { + template <> const ItemKey payloadGetKey(const ModelMeshPartPayload::Pointer& payload) { + if (payload) { + return payload->getKey(); + } + // Return opaque for lack of a better idea + return ItemKey::Builder::opaqueShape(); + } + + template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointer& payload) { + if (payload) { + return payload->getBound(); + } + return render::Item::Bound(); + } + template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args) { + return payload->render(args); + } +} + +using namespace render; + +ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : + _model(model), + _meshIndex(_meshIndex), + _shapeID(shapeIndex) { + auto& modelMesh = _model->_geometry->getMeshes().at(_meshIndex)->_mesh; + updateMeshPart(modelMesh, partIndex); + + updateTransform(transform, offsetTransform); + initCache(); +} + +void ModelMeshPartPayload::initCache() { + if (_drawMesh) { + auto vertexFormat = _drawMesh->getVertexFormat(); + _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); + _isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX); + + + const FBXGeometry& geometry = _model->_geometry->getFBXGeometry(); + const FBXMesh& mesh = geometry.meshes.at(_meshIndex); + _isBlendShaped = !mesh.blendshapes.isEmpty(); + } + + auto networkMaterial = _model->_geometry->getShapeMaterial(_shapeID); + if (networkMaterial) { + _drawMaterial = networkMaterial->_material; + }; + +} + + +void ModelMeshPartPayload::notifyLocationChanged() { + _model->_needsUpdateClusterMatrices = true; +} + +render::ItemKey ModelMeshPartPayload::getKey() const { + ItemKey::Builder builder; + builder.withTypeShape(); + + if (!_model->isVisible()) { + builder.withInvisible(); + } + + if (_isBlendShaped || _isSkinned) { + builder.withDeformed(); + } + + if (_drawMaterial) { + auto matKey = _drawMaterial->getKey(); + if (matKey.isTransparent() || matKey.isTransparentMap()) { + builder.withTransparent(); + } + } + + return builder.build(); +} + +render::Item::Bound ModelMeshPartPayload::getBound() const { + // NOTE: we can't cache this bounds because we need to handle the case of a moving + // entity or mesh part. + return _model->getPartBounds(_meshIndex, _partIndex, _transform.getTranslation(), _transform.getRotation()); +} + +void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { + if (!_isBlendShaped) { + batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); + + batch.setInputFormat((_drawMesh->getVertexFormat())); + + batch.setInputStream(0, _drawMesh->getVertexStream()); + } else { + batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); + + batch.setInputFormat((_drawMesh->getVertexFormat())); + + batch.setInputBuffer(0, _model->_blendedVertexBuffers[_meshIndex], 0, sizeof(glm::vec3)); + batch.setInputBuffer(1, _model->_blendedVertexBuffers[_meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); + batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); + } + + // TODO: Get rid of that extra call + if (!_hasColorAttrib) { + batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } +} + +void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const { + // Still relying on the raw data from the model + const Model::MeshState& state = _model->_meshStates.at(_meshIndex); + + Transform transform; + if (state.clusterBuffer) { + if (_model->_cauterizeBones) { + batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.cauterizedClusterBuffer); + } else { + batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.clusterBuffer); + } + } else { + if (_model->_cauterizeBones) { + transform = Transform(state.cauterizedClusterMatrices[0]); + } else { + transform = Transform(state.clusterMatrices[0]); + } + } + // transform.preTranslate(_modelPosition); + transform.preTranslate(_transform.getTranslation()); + batch.setModelTransform(transform); +} + + +void ModelMeshPartPayload::render(RenderArgs* args) const { + PerformanceTimer perfTimer("ModelMeshPartPayload::render"); + if (!_model->_readyWhenAdded || !_model->_isVisible) { return; // bail asap } @@ -248,25 +398,25 @@ void MeshPartPayload::render(RenderArgs* args) const { auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME - const FBXGeometry& geometry = model->_geometry->getFBXGeometry(); - const std::vector>& networkMeshes = model->_geometry->getMeshes(); + const FBXGeometry& geometry = _model->_geometry->getFBXGeometry(); + const std::vector>& networkMeshes = _model->_geometry->getMeshes(); // guard against partially loaded meshes - if (meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)geometry.meshes.size() || meshIndex >= (int)model->_meshStates.size() ) { + if (_meshIndex >= (int)networkMeshes.size() || _meshIndex >= (int)geometry.meshes.size() || _meshIndex >= (int)_model->_meshStates.size() ) { return; } // Back to model to update the cluster matrices right now - model->updateClusterMatrices(_modelPosition, _modelOrientation); + _model->updateClusterMatrices(_transform.getTranslation(), _transform.getRotation()); - const FBXMesh& mesh = geometry.meshes.at(meshIndex); + const FBXMesh& mesh = geometry.meshes.at(_meshIndex); // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown // to false to rebuild out mesh groups. - if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex > geometry.meshes.size()) { - model->_meshGroupsKnown = false; // regenerate these lists next time around. - model->_readyWhenAdded = false; // in case any of our users are using scenes - model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid + if (_meshIndex < 0 || _meshIndex >= (int)networkMeshes.size() || _meshIndex > geometry.meshes.size()) { + _model->_meshGroupsKnown = false; // regenerate these lists next time around. + _model->_readyWhenAdded = false; // in case any of our users are using scenes + _model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid return; // FIXME! } @@ -276,13 +426,7 @@ void MeshPartPayload::render(RenderArgs* args) const { // sanity check return; // FIXME! } - - - // guard against partially loaded meshes - if (partIndex >= mesh.parts.size()) { - return; - } - + model::MaterialKey drawMaterialKey; if (_drawMaterial) { drawMaterialKey = _drawMaterial->getKey(); @@ -293,12 +437,12 @@ void MeshPartPayload::render(RenderArgs* args) const { bool hasSpecular = drawMaterialKey.isGlossMap(); bool hasLightmap = drawMaterialKey.isLightmapMap(); bool isSkinned = _isSkinned; - bool wireframe = model->isWireframe(); + bool wireframe = _model->isWireframe(); // render the part bounding box #ifdef DEBUG_BOUNDING_PARTS { - AABox partBounds = getPartBounds(meshIndex, partIndex); + AABox partBounds = getPartBounds(_meshIndex, partIndex); bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE; glm::vec4 cubeColor; diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index b29d9510d1..e03d1c8a68 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -1,5 +1,5 @@ // -// MeshPartPayload.h +// ModelMeshPartPayload.h // interface/src/renderer // // Created by Sam Gateau on 10/3/15. @@ -24,41 +24,44 @@ class Model; class MeshPartPayload { public: - MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, glm::vec3 position, glm::quat orientation); - + MeshPartPayload() {} + MeshPartPayload(model::MeshPointer mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform); + typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - Model* model; - int meshIndex; - int partIndex; - int _shapeID; - glm::vec3 _modelPosition; - glm::quat _modelOrientation; + virtual void updateMeshPart(model::MeshPointer drawMesh, int partIndex); - void updateModelLocation(glm::vec3 position, glm::quat orientation); + virtual void notifyLocationChanged() {} + virtual void updateTransform(const Transform& transform, const Transform& offsetTransform); + + virtual void updateMaterial(model::MaterialPointer drawMaterial); // Render Item interface - render::ItemKey getKey() const; - render::Item::Bound getBound() const; - void render(RenderArgs* args) const; - - // MeshPartPayload functions to perform render + virtual render::ItemKey getKey() const; + virtual render::Item::Bound getBound() const; + virtual void render(RenderArgs* args) const; + + // ModelMeshPartPayload functions to perform render void drawCall(gpu::Batch& batch) const; - void bindMesh(gpu::Batch& batch) const; - void bindMaterial(gpu::Batch& batch, const ModelRender::Locations* locations) const; - void bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const; - - - void initCache(); + virtual void bindMesh(gpu::Batch& batch) const; + virtual void bindMaterial(gpu::Batch& batch, const ModelRender::Locations* locations) const; + virtual void bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const; // Payload resource cached values model::MeshPointer _drawMesh; + int _partIndex = 0; model::Mesh::Part _drawPart; + model::MaterialPointer _drawMaterial; + + model::Box _localBound; + Transform _drawTransform; + Transform _transform; + Transform _offsetTransform; + mutable model::Box _worldBound; + bool _hasColorAttrib = false; - bool _isSkinned = false; - bool _isBlendShaped = false; }; namespace render { @@ -67,4 +70,32 @@ namespace render { template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args); } + +class ModelMeshPartPayload : public MeshPartPayload { +public: + ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); + + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + void notifyLocationChanged() override; + + // Render Item interface + render::ItemKey getKey() const override; + render::Item::Bound getBound() const override; + void render(RenderArgs* args) const override; + + // ModelMeshPartPayload functions to perform render + void bindMesh(gpu::Batch& batch) const override; + void bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const override; + + + void initCache(); + Model* _model; + int _meshIndex; + int _shapeID; + bool _isSkinned = false; + bool _isBlendShaped = false; +}; + #endif // hifi_MeshPartPayload_h diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b367d299e5..e7c4442755 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -90,7 +90,7 @@ void Model::setScale(const glm::vec3& scale) { _scaledToFit = false; } -const float METERS_PER_MILLIMETER = 0.01f; +const float METERS_PER_MILLIMETER = 0.01f; void Model::setScaleInternal(const glm::vec3& scale) { if (glm::distance(_scale, scale) > METERS_PER_MILLIMETER) { @@ -110,11 +110,19 @@ void Model::setOffset(const glm::vec3& offset) { void Model::enqueueLocationChange() { render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + Transform transform; + transform.setTranslation(_translation); + transform.setRotation(_rotation); + + Transform offset; + offset.setScale(_scale); + offset.postTranslate(_offset); + render::PendingChanges pendingChanges; foreach (auto itemID, _renderItems.keys()) { pendingChanges.updateItem(itemID, [=](MeshPartPayload& data) { - data.updateModelLocation(_translation, _rotation); - data.model->_needsUpdateClusterMatrices = true; + data.updateTransform(transform, offset); + data.notifyLocationChanged(); }); } @@ -495,11 +503,10 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan foreach (auto renderItem, _renderItemsSet) { auto item = scene->allocateID(); - auto renderData = MeshPartPayload::Pointer(renderItem); - auto renderPayload = std::make_shared(renderData); + auto renderPayload = std::make_shared(renderItem); pendingChanges.resetItem(item, renderPayload); pendingChanges.updateItem(item, [&](MeshPartPayload& data) { - data.model->_needsUpdateClusterMatrices = true; + data.notifyLocationChanged(); }); _renderItems.insert(item, renderPayload); somethingAdded = true; @@ -523,12 +530,11 @@ bool Model::addToScene(std::shared_ptr scene, foreach (auto renderItem, _renderItemsSet) { auto item = scene->allocateID(); - auto renderData = MeshPartPayload::Pointer(renderItem); - auto renderPayload = std::make_shared(renderData); + auto renderPayload = std::make_shared(renderItem); renderPayload->addStatusGetters(statusGetters); pendingChanges.resetItem(item, renderPayload); pendingChanges.updateItem(item, [&](MeshPartPayload& data) { - data.model->_needsUpdateClusterMatrices = true; + data.notifyLocationChanged(); }); _renderItems.insert(item, renderPayload); somethingAdded = true; @@ -1127,8 +1133,14 @@ AABox Model::getPartBounds(int meshIndex, int partIndex, glm::vec3 modelPosition void Model::segregateMeshGroups() { QSharedPointer networkGeometry; - if (_showCollisionHull && _collisionGeometry && _collisionGeometry->isLoaded()) { - networkGeometry = _collisionGeometry; + bool showingCollisionHull = false; + if (_showCollisionHull && _collisionGeometry) { + if (_collisionGeometry->isLoaded()) { + networkGeometry = _collisionGeometry; + showingCollisionHull = true; + } else { + return; + } } else { networkGeometry = _geometry; } @@ -1136,8 +1148,10 @@ void Model::segregateMeshGroups() { const std::vector>& networkMeshes = networkGeometry->getMeshes(); // all of our mesh vectors must match in size - if ((int)networkMeshes.size() != geometry.meshes.size() || - geometry.meshes.size() != _meshStates.size()) { + auto geoMeshesSize = geometry.meshes.size(); + if ((int)networkMeshes.size() != geoMeshesSize || + // geometry.meshes.size() != _meshStates.size()) { + geoMeshesSize > _meshStates.size()) { qDebug() << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; return; } @@ -1147,15 +1161,30 @@ void Model::segregateMeshGroups() { _renderItemsSet.clear(); + Transform transform; + transform.setTranslation(_translation); + transform.setRotation(_rotation); + + Transform offset; + offset.setScale(_scale); + offset.postTranslate(_offset); + // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; for (int i = 0; i < (int)networkMeshes.size(); i++) { const FBXMesh& mesh = geometry.meshes.at(i); + const NetworkMesh& networkMesh = *(networkMeshes.at(i).get()); // Create the render payloads int totalParts = mesh.parts.size(); for (int partIndex = 0; partIndex < totalParts; partIndex++) { - _renderItemsSet << std::make_shared(this, i, partIndex, shapeID, _translation, _rotation); + if (showingCollisionHull) { + _renderItemsSet << std::make_shared(networkMesh._mesh, partIndex, ModelRender::getCollisionHullMaterial(), transform, offset); + + } else { + _renderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset); + } + shapeID++; } } @@ -1168,15 +1197,22 @@ bool Model::initWhenReady(render::ScenePointer scene) { render::PendingChanges pendingChanges; + Transform transform; + transform.setTranslation(_translation); + transform.setRotation(_rotation); + + Transform offset; + offset.setScale(_scale); + offset.postTranslate(_offset); + foreach (auto renderItem, _renderItemsSet) { auto item = scene->allocateID(); - auto renderData = MeshPartPayload::Pointer(renderItem); - auto renderPayload = std::make_shared(renderData); + auto renderPayload = std::make_shared(renderItem); _renderItems.insert(item, renderPayload); pendingChanges.resetItem(item, renderPayload); pendingChanges.updateItem(item, [&](MeshPartPayload& data) { - data.updateModelLocation(_translation, _rotation); - data.model->_needsUpdateClusterMatrices = true; + data.updateTransform(transform, offset); + data.notifyLocationChanged(); }); } scene->enqueuePendingChanges(pendingChanges); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index d77cf830bd..08abb74316 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -366,7 +366,7 @@ private: bool _needsUpdateClusterMatrices = true; bool _showCollisionHull = false; - friend class MeshPartPayload; + friend class ModelMeshPartPayload; protected: RigPointer _rig; }; diff --git a/libraries/render-utils/src/ModelRender.cpp b/libraries/render-utils/src/ModelRender.cpp index 73f3d715b6..10c9d738d2 100644 --- a/libraries/render-utils/src/ModelRender.cpp +++ b/libraries/render-utils/src/ModelRender.cpp @@ -280,3 +280,16 @@ void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, b DependencyManager::get()->getNormalFittingTexture()); } } + +model::MaterialPointer ModelRender::_collisionHullMaterial; + +model::MaterialPointer ModelRender::getCollisionHullMaterial() { + if (!_collisionHullMaterial) { + _collisionHullMaterial = std::make_shared(); + _collisionHullMaterial->setDiffuse(glm::vec3(1.0f, 0.5f, 0.0f)); + _collisionHullMaterial->setMetallic(0.02f); + _collisionHullMaterial->setGloss(1.0f); + } + return _collisionHullMaterial; +} + diff --git a/libraries/render-utils/src/ModelRender.h b/libraries/render-utils/src/ModelRender.h index 1528dcfc87..39fe05378d 100644 --- a/libraries/render-utils/src/ModelRender.h +++ b/libraries/render-utils/src/ModelRender.h @@ -149,6 +149,11 @@ public: static const RenderPipelineLib& getRenderPipelineLib(); + // Collision hull Material + static model::MaterialPointer _collisionHullMaterial; + + static model::MaterialPointer getCollisionHullMaterial(); + }; #endif // hifi_ModelRender_h \ No newline at end of file diff --git a/libraries/shared/src/AABox.cpp b/libraries/shared/src/AABox.cpp index 7c001c570e..d16bdafcec 100644 --- a/libraries/shared/src/AABox.cpp +++ b/libraries/shared/src/AABox.cpp @@ -12,6 +12,7 @@ #include "AABox.h" #include "AACube.h" +#include "Transform.h" #include "Extents.h" #include "GeometryUtil.h" #include "NumericalConstants.h" @@ -42,50 +43,6 @@ glm::vec3 AABox::calcCenter() const { return center; } -void AABox::rotate(const glm::quat& rotation) { - auto minimum = _corner; - auto maximum = _corner + _scale; - - glm::vec3 bottomLeftNear(minimum.x, minimum.y, minimum.z); - glm::vec3 bottomRightNear(maximum.x, minimum.y, minimum.z); - glm::vec3 bottomLeftFar(minimum.x, minimum.y, maximum.z); - glm::vec3 bottomRightFar(maximum.x, minimum.y, maximum.z); - glm::vec3 topLeftNear(minimum.x, maximum.y, minimum.z); - glm::vec3 topRightNear(maximum.x, maximum.y, minimum.z); - glm::vec3 topLeftFar(minimum.x, maximum.y, maximum.z); - glm::vec3 topRightFar(maximum.x, maximum.y, maximum.z); - - glm::vec3 bottomLeftNearRotated = rotation * bottomLeftNear; - glm::vec3 bottomRightNearRotated = rotation * bottomRightNear; - glm::vec3 bottomLeftFarRotated = rotation * bottomLeftFar; - glm::vec3 bottomRightFarRotated = rotation * bottomRightFar; - glm::vec3 topLeftNearRotated = rotation * topLeftNear; - glm::vec3 topRightNearRotated = rotation * topRightNear; - glm::vec3 topLeftFarRotated = rotation * topLeftFar; - glm::vec3 topRightFarRotated = rotation * topRightFar; - - minimum = glm::min(bottomLeftNearRotated, - glm::min(bottomRightNearRotated, - glm::min(bottomLeftFarRotated, - glm::min(bottomRightFarRotated, - glm::min(topLeftNearRotated, - glm::min(topRightNearRotated, - glm::min(topLeftFarRotated, - topRightFarRotated))))))); - - maximum = glm::max(bottomLeftNearRotated, - glm::max(bottomRightNearRotated, - glm::max(bottomLeftFarRotated, - glm::max(bottomRightFarRotated, - glm::max(topLeftNearRotated, - glm::max(topRightNearRotated, - glm::max(topLeftFarRotated, - topRightFarRotated))))))); - - _corner = minimum; - _scale = maximum - minimum; -} - glm::vec3 AABox::getVertex(BoxVertex vertex) const { switch (vertex) { case BOTTOM_LEFT_NEAR: @@ -525,3 +482,59 @@ AABox& AABox::operator += (const AABox& box) { } return (*this); } + +void AABox::scale(const glm::vec3& scale) { + _corner *= scale; + _scale *= scale; +} + + +void AABox::rotate(const glm::quat& rotation) { + auto minimum = _corner; + auto maximum = _corner + _scale; + + glm::vec3 bottomLeftNear(minimum.x, minimum.y, minimum.z); + glm::vec3 bottomRightNear(maximum.x, minimum.y, minimum.z); + glm::vec3 bottomLeftFar(minimum.x, minimum.y, maximum.z); + glm::vec3 bottomRightFar(maximum.x, minimum.y, maximum.z); + glm::vec3 topLeftNear(minimum.x, maximum.y, minimum.z); + glm::vec3 topRightNear(maximum.x, maximum.y, minimum.z); + glm::vec3 topLeftFar(minimum.x, maximum.y, maximum.z); + glm::vec3 topRightFar(maximum.x, maximum.y, maximum.z); + + glm::vec3 bottomLeftNearRotated = rotation * bottomLeftNear; + glm::vec3 bottomRightNearRotated = rotation * bottomRightNear; + glm::vec3 bottomLeftFarRotated = rotation * bottomLeftFar; + glm::vec3 bottomRightFarRotated = rotation * bottomRightFar; + glm::vec3 topLeftNearRotated = rotation * topLeftNear; + glm::vec3 topRightNearRotated = rotation * topRightNear; + glm::vec3 topLeftFarRotated = rotation * topLeftFar; + glm::vec3 topRightFarRotated = rotation * topRightFar; + + minimum = glm::min(bottomLeftNearRotated, + glm::min(bottomRightNearRotated, + glm::min(bottomLeftFarRotated, + glm::min(bottomRightFarRotated, + glm::min(topLeftNearRotated, + glm::min(topRightNearRotated, + glm::min(topLeftFarRotated, + topRightFarRotated))))))); + + maximum = glm::max(bottomLeftNearRotated, + glm::max(bottomRightNearRotated, + glm::max(bottomLeftFarRotated, + glm::max(bottomRightFarRotated, + glm::max(topLeftNearRotated, + glm::max(topRightNearRotated, + glm::max(topLeftFarRotated, + topRightFarRotated))))))); + + _corner = minimum; + _scale = maximum - minimum; +} + +void AABox::transform(const Transform& transform) { + scale(transform.getScale()); + rotate(transform.getRotation()); + translate(transform.getTranslation()); +} \ No newline at end of file diff --git a/libraries/shared/src/AABox.h b/libraries/shared/src/AABox.h index 8de0462138..24791445b3 100644 --- a/libraries/shared/src/AABox.h +++ b/libraries/shared/src/AABox.h @@ -24,6 +24,7 @@ class AACube; class Extents; +class Transform; class AABox { @@ -40,12 +41,7 @@ public: void setBox(const glm::vec3& corner, float scale); glm::vec3 getVertexP(const glm::vec3& normal) const; glm::vec3 getVertexN(const glm::vec3& normal) const; - - void shiftBy(const glm::vec3& delta) { _corner += delta; } - void rotate(const glm::quat& rotation); - void scale(float scale) { _corner *= scale; _scale *= scale; } - void scale(const glm::vec3& scale) { _corner *= scale; _scale *= scale; } - + const glm::vec3& getCorner() const { return _corner; } const glm::vec3& getScale() const { return _scale; } const glm::vec3& getDimensions() const { return _scale; } @@ -85,6 +81,20 @@ public: AABox& operator += (const glm::vec3& point); AABox& operator += (const AABox& box); + // Translate the AABox just moving the corner + void translate(const glm::vec3& translation) { _corner += translation; } + + // Rotate the AABox around its frame origin + // meaning rotating the corners of the AABox around the point {0,0,0} and reevaluating the min max + void rotate(const glm::quat& rotation); + + /// Scale the AABox + void scale(float scale); + void scale(const glm::vec3& scale); + + // Transform the extents with transform + void transform(const Transform& transform); + bool isInvalid() const { return _corner == glm::vec3(std::numeric_limits::infinity()); } private: