mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #7487 from hyperlogic/tony/animated-culling
Improved render bounding boxes for animated models
This commit is contained in:
commit
d99dcf5450
17 changed files with 300 additions and 81 deletions
|
@ -2975,6 +2975,11 @@ void Application::updateLOD() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::pushPreRenderLambda(void* key, std::function<void()> func) {
|
||||
std::unique_lock<std::mutex> guard(_preRenderLambdasLock);
|
||||
_preRenderLambdas[key] = func;
|
||||
}
|
||||
|
||||
// Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone.
|
||||
// (Maybe this code should be moved there?)
|
||||
// The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition().
|
||||
|
@ -3451,6 +3456,16 @@ void Application::update(float deltaTime) {
|
|||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "sendDownstreamAudioStatsPacket", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE_EX("PreRenderLambdas", 0xffff0000, (uint64_t)0);
|
||||
|
||||
std::unique_lock<std::mutex> guard(_preRenderLambdasLock);
|
||||
for (auto& iter : _preRenderLambdas) {
|
||||
iter.second();
|
||||
}
|
||||
_preRenderLambdas.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -211,6 +211,8 @@ public:
|
|||
render::EnginePointer getRenderEngine() override { return _renderEngine; }
|
||||
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
||||
|
||||
virtual void pushPreRenderLambda(void* key, std::function<void()> func) override;
|
||||
|
||||
const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
|
||||
|
||||
void updateMyAvatarLookAtPosition();
|
||||
|
@ -510,6 +512,9 @@ private:
|
|||
bool _cursorNeedsChanging { false };
|
||||
|
||||
QThread* _deadlockWatchdogThread;
|
||||
|
||||
std::map<void*, std::function<void()>> _preRenderLambdas;
|
||||
std::mutex _preRenderLambdasLock;
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -18,22 +18,22 @@
|
|||
QString const ModelOverlay::TYPE = "model";
|
||||
|
||||
ModelOverlay::ModelOverlay()
|
||||
: _model(std::make_shared<Rig>()),
|
||||
: _model(std::make_shared<Model>(std::make_shared<Rig>())),
|
||||
_modelTextures(QVariantMap()),
|
||||
_updateModel(false)
|
||||
{
|
||||
_model.init();
|
||||
_model->init();
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
|
||||
Volume3DOverlay(modelOverlay),
|
||||
_model(std::make_shared<Rig>()),
|
||||
_model(std::make_shared<Model>(std::make_shared<Rig>())),
|
||||
_modelTextures(QVariantMap()),
|
||||
_url(modelOverlay->_url),
|
||||
_updateModel(false)
|
||||
{
|
||||
_model.init();
|
||||
_model->init();
|
||||
if (_url.isValid()) {
|
||||
_updateModel = true;
|
||||
_isLoaded = false;
|
||||
|
@ -44,27 +44,27 @@ void ModelOverlay::update(float deltatime) {
|
|||
if (_updateModel) {
|
||||
_updateModel = false;
|
||||
|
||||
_model.setSnapModelToCenter(true);
|
||||
_model.setScale(getDimensions());
|
||||
_model.setRotation(getRotation());
|
||||
_model.setTranslation(getPosition());
|
||||
_model.setURL(_url);
|
||||
_model.simulate(deltatime, true);
|
||||
_model->setSnapModelToCenter(true);
|
||||
_model->setScale(getDimensions());
|
||||
_model->setRotation(getRotation());
|
||||
_model->setTranslation(getPosition());
|
||||
_model->setURL(_url);
|
||||
_model->simulate(deltatime, true);
|
||||
} else {
|
||||
_model.simulate(deltatime);
|
||||
_model->simulate(deltatime);
|
||||
}
|
||||
_isLoaded = _model.isActive();
|
||||
_isLoaded = _model->isActive();
|
||||
}
|
||||
|
||||
bool ModelOverlay::addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
||||
Volume3DOverlay::addToScene(overlay, scene, pendingChanges);
|
||||
_model.addToScene(scene, pendingChanges);
|
||||
_model->addToScene(scene, pendingChanges);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModelOverlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
||||
Volume3DOverlay::removeFromScene(overlay, scene, pendingChanges);
|
||||
_model.removeFromScene(scene, pendingChanges);
|
||||
_model->removeFromScene(scene, pendingChanges);
|
||||
}
|
||||
|
||||
void ModelOverlay::render(RenderArgs* args) {
|
||||
|
@ -73,9 +73,9 @@ void ModelOverlay::render(RenderArgs* args) {
|
|||
// fix them up in the scene
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::PendingChanges pendingChanges;
|
||||
if (_model.needsFixupInScene()) {
|
||||
_model.removeFromScene(scene, pendingChanges);
|
||||
_model.addToScene(scene, pendingChanges);
|
||||
if (_model->needsFixupInScene()) {
|
||||
_model->removeFromScene(scene, pendingChanges);
|
||||
_model->addToScene(scene, pendingChanges);
|
||||
}
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
|
||||
|
@ -100,7 +100,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
|
|||
if (newScale.x <= 0 || newScale.y <= 0 || newScale.z <= 0) {
|
||||
setDimensions(scale);
|
||||
} else {
|
||||
_model.setScaleToFit(true, getDimensions());
|
||||
_model->setScaleToFit(true, getDimensions());
|
||||
_updateModel = true;
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
|
|||
QUrl newTextureURL = textureMap[key].toUrl();
|
||||
qDebug() << "Updating texture named" << key << "to texture at URL" << newTextureURL;
|
||||
|
||||
QMetaObject::invokeMethod(&_model, "setTextureWithNameToURL", Qt::AutoConnection,
|
||||
QMetaObject::invokeMethod(_model.get(), "setTextureWithNameToURL", Qt::AutoConnection,
|
||||
Q_ARG(const QString&, key),
|
||||
Q_ARG(const QUrl&, newTextureURL));
|
||||
|
||||
|
@ -134,7 +134,7 @@ QVariant ModelOverlay::getProperty(const QString& property) {
|
|||
return _url.toString();
|
||||
}
|
||||
if (property == "dimensions" || property == "scale" || property == "size") {
|
||||
return vec3toVariant(_model.getScaleToFitDimensions());
|
||||
return vec3toVariant(_model->getScaleToFitDimensions());
|
||||
}
|
||||
if (property == "textures") {
|
||||
if (_modelTextures.size() > 0) {
|
||||
|
@ -155,13 +155,13 @@ bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3&
|
|||
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
|
||||
|
||||
QString subMeshNameTemp;
|
||||
return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, subMeshNameTemp);
|
||||
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, subMeshNameTemp);
|
||||
}
|
||||
|
||||
bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QString& extraInfo) {
|
||||
|
||||
return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo);
|
||||
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo);
|
||||
}
|
||||
|
||||
ModelOverlay* ModelOverlay::createClone() const {
|
||||
|
|
|
@ -41,11 +41,11 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
Model _model;
|
||||
ModelPointer _model;
|
||||
QVariantMap _modelTextures;
|
||||
|
||||
QUrl _url;
|
||||
bool _updateModel;
|
||||
};
|
||||
|
||||
#endif // hifi_ModelOverlay_h
|
||||
#endif // hifi_ModelOverlay_h
|
||||
|
|
|
@ -1056,7 +1056,9 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
|
|||
|
||||
// limit rotation
|
||||
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
|
||||
deltaQuat = glm::angleAxis(glm::clamp(glm::angle(deltaQuat), -MAX_ANGLE, MAX_ANGLE), glm::axis(deltaQuat));
|
||||
if (fabsf(glm::angle(deltaQuat)) > MAX_ANGLE) {
|
||||
deltaQuat = glm::angleAxis(glm::clamp(glm::angle(deltaQuat), -MAX_ANGLE, MAX_ANGLE), glm::axis(deltaQuat));
|
||||
}
|
||||
|
||||
// directly set absolutePose rotation
|
||||
_internalPoseSet._absolutePoses[index].rot = deltaQuat * headQuat;
|
||||
|
|
|
@ -221,6 +221,8 @@ public:
|
|||
|
||||
void setEnableInverseKinematics(bool enable);
|
||||
|
||||
const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; }
|
||||
|
||||
protected:
|
||||
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
|
||||
void updateAnimationStateHandlers();
|
||||
|
|
|
@ -111,7 +111,7 @@ Box Mesh::evalPartBound(int partNum) const {
|
|||
return box;
|
||||
}
|
||||
|
||||
Box Mesh::evalPartBounds(int partStart, int partEnd, Boxes& bounds) const {
|
||||
Box Mesh::evalPartsBound(int partStart, int partEnd) const {
|
||||
Box totalBound;
|
||||
auto part = _partBuffer.cbegin<Part>() + partStart;
|
||||
auto partItEnd = _partBuffer.cbegin<Part>() + partEnd;
|
||||
|
|
|
@ -108,9 +108,9 @@ public:
|
|||
|
||||
// evaluate the bounding box of A part
|
||||
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.
|
||||
Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const;
|
||||
// evaluate the bounding boxes of the parts in the range [start, end]
|
||||
// the returned box is the bounding box of ALL the evaluated parts bound.
|
||||
Box evalPartsBound(int partStart, int partEnd) const;
|
||||
|
||||
static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast<gpu::Primitive>(topo); }
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ public:
|
|||
virtual render::ScenePointer getMain3DScene() = 0;
|
||||
virtual render::EnginePointer getRenderEngine() = 0;
|
||||
|
||||
virtual void pushPreRenderLambda(void* key, std::function<void()> func) = 0;
|
||||
|
||||
// FIXME - we shouldn't assume that there's a single instance of an AbstractViewStateInterface
|
||||
static AbstractViewStateInterface* instance();
|
||||
static void setInstance(AbstractViewStateInterface* instance);
|
||||
|
|
|
@ -58,7 +58,6 @@ void MeshPartPayload::updateMeshPart(const std::shared_ptr<const model::Mesh>& d
|
|||
auto vertexFormat = _drawMesh->getVertexFormat();
|
||||
_hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR);
|
||||
_drawPart = _drawMesh->getPartBuffer().get<model::Mesh::Part>(partIndex);
|
||||
|
||||
_localBound = _drawMesh->evalPartBound(partIndex);
|
||||
}
|
||||
}
|
||||
|
@ -352,7 +351,23 @@ void ModelMeshPartPayload::initCache() {
|
|||
|
||||
|
||||
void ModelMeshPartPayload::notifyLocationChanged() {
|
||||
_model->_needsUpdateClusterMatrices = true;
|
||||
|
||||
}
|
||||
|
||||
void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector<glm::mat4>& clusterMatrices) {
|
||||
ModelMeshPartPayload::updateTransform(transform, offsetTransform);
|
||||
|
||||
if (clusterMatrices.size() > 0) {
|
||||
_worldBound = AABox();
|
||||
for (auto& clusterMatrix : clusterMatrices) {
|
||||
AABox clusterBound = _localBound;
|
||||
clusterBound.transform(clusterMatrix);
|
||||
_worldBound += clusterBound;
|
||||
}
|
||||
|
||||
// clusterMatrix has world rotation but not world translation.
|
||||
_worldBound.translate(transform.getTranslation());
|
||||
}
|
||||
}
|
||||
|
||||
ItemKey ModelMeshPartPayload::getKey() const {
|
||||
|
@ -377,12 +392,6 @@ ItemKey ModelMeshPartPayload::getKey() const {
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
ShapeKey ModelMeshPartPayload::getShapeKey() const {
|
||||
assert(_model->isLoaded());
|
||||
const FBXGeometry& geometry = _model->getFBXGeometry();
|
||||
|
|
|
@ -74,15 +74,15 @@ namespace render {
|
|||
class ModelMeshPartPayload : public MeshPartPayload {
|
||||
public:
|
||||
ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
|
||||
|
||||
|
||||
typedef render::Payload<ModelMeshPartPayload> Payload;
|
||||
typedef Payload::DataPointer Pointer;
|
||||
|
||||
void notifyLocationChanged() override;
|
||||
void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector<glm::mat4>& clusterMatrices);
|
||||
|
||||
// Render Item interface
|
||||
render::ItemKey getKey() const override;
|
||||
render::Item::Bound getBound() const override;
|
||||
render::ShapeKey getShapeKey() const override; // shape interface
|
||||
void render(RenderArgs* args) const override;
|
||||
|
||||
|
@ -101,4 +101,11 @@ public:
|
|||
bool _isBlendShaped{ false };
|
||||
};
|
||||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const ModelMeshPartPayload::Pointer& payload);
|
||||
template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointer& payload);
|
||||
template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload);
|
||||
template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args);
|
||||
}
|
||||
|
||||
#endif // hifi_MeshPartPayload_h
|
||||
|
|
|
@ -130,25 +130,60 @@ void Model::setOffset(const glm::vec3& offset) {
|
|||
}
|
||||
|
||||
void Model::enqueueLocationChange() {
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(_translation);
|
||||
transform.setRotation(_rotation);
|
||||
// queue up this work for later processing, at the end of update and just before rendering.
|
||||
// the application will ensure only the last lambda is actually invoked.
|
||||
void* key = (void*)this;
|
||||
std::weak_ptr<Model> weakSelf = shared_from_this();
|
||||
AbstractViewStateInterface::instance()->pushPreRenderLambda(key, [weakSelf]() {
|
||||
|
||||
Transform offset;
|
||||
offset.setScale(_scale);
|
||||
offset.postTranslate(_offset);
|
||||
// do nothing, if the model has already been destroyed.
|
||||
auto self = weakSelf.lock();
|
||||
if (!self) {
|
||||
return;
|
||||
}
|
||||
|
||||
render::PendingChanges pendingChanges;
|
||||
foreach (auto itemID, _renderItems.keys()) {
|
||||
pendingChanges.updateItem<MeshPartPayload>(itemID, [transform, offset](MeshPartPayload& data) {
|
||||
data.updateTransform(transform, offset);
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
}
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
Transform modelTransform;
|
||||
modelTransform.setScale(self->_scale);
|
||||
modelTransform.setTranslation(self->_translation);
|
||||
modelTransform.setRotation(self->_rotation);
|
||||
|
||||
Transform modelMeshOffset;
|
||||
if (self->isLoaded()) {
|
||||
// includes model offset and unitScale.
|
||||
modelMeshOffset = Transform(self->_rig->getGeometryToRigTransform());
|
||||
} else {
|
||||
modelMeshOffset.postTranslate(self->_offset);
|
||||
}
|
||||
|
||||
// only apply offset only, collision mesh does not share the same unit scale as the FBX file's mesh.
|
||||
Transform collisionMeshOffset;
|
||||
collisionMeshOffset.postTranslate(self->_offset);
|
||||
|
||||
render::PendingChanges pendingChanges;
|
||||
foreach (auto itemID, self->_modelMeshRenderItems.keys()) {
|
||||
pendingChanges.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, modelMeshOffset](ModelMeshPartPayload& data) {
|
||||
|
||||
// lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box.
|
||||
data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation());
|
||||
|
||||
// update the model transform and bounding box for this render item.
|
||||
const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex);
|
||||
data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, state.clusterMatrices);
|
||||
});
|
||||
}
|
||||
|
||||
foreach (auto itemID, self->_collisionRenderItems.keys()) {
|
||||
pendingChanges.updateItem<MeshPartPayload>(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) {
|
||||
// update the model transform for this render item.
|
||||
data.updateTransform(modelTransform, collisionMeshOffset);
|
||||
});
|
||||
}
|
||||
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
});
|
||||
}
|
||||
|
||||
void Model::initJointTransforms() {
|
||||
|
@ -497,8 +532,11 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scen
|
|||
_isVisible = newValue;
|
||||
|
||||
render::PendingChanges pendingChanges;
|
||||
foreach (auto item, _renderItems.keys()) {
|
||||
pendingChanges.resetItem(item, _renderItems[item]);
|
||||
foreach (auto item, _modelMeshRenderItems.keys()) {
|
||||
pendingChanges.resetItem(item, _modelMeshRenderItems[item]);
|
||||
}
|
||||
foreach (auto item, _collisionRenderItems.keys()) {
|
||||
pendingChanges.resetItem(item, _modelMeshRenderItems[item]);
|
||||
}
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
}
|
||||
|
@ -514,14 +552,25 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChan
|
|||
|
||||
bool somethingAdded = false;
|
||||
|
||||
foreach (auto renderItem, _renderItemsSet) {
|
||||
foreach (auto renderItem, _modelMeshRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
_modelMeshRenderItems.insert(item, renderPayload);
|
||||
somethingAdded = true;
|
||||
}
|
||||
|
||||
foreach (auto renderItem, _collisionRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
_renderItems.insert(item, renderPayload);
|
||||
_collisionRenderItems.insert(item, renderPayload);
|
||||
somethingAdded = true;
|
||||
}
|
||||
|
||||
|
@ -541,7 +590,19 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
|||
|
||||
bool somethingAdded = false;
|
||||
|
||||
foreach (auto renderItem, _renderItemsSet) {
|
||||
foreach (auto renderItem, _modelMeshRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
_modelMeshRenderItems.insert(item, renderPayload);
|
||||
somethingAdded = true;
|
||||
}
|
||||
|
||||
foreach (auto renderItem, _collisionRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
|
@ -549,7 +610,7 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
|||
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
_renderItems.insert(item, renderPayload);
|
||||
_collisionRenderItems.insert(item, renderPayload);
|
||||
somethingAdded = true;
|
||||
}
|
||||
|
||||
|
@ -559,11 +620,16 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
|||
}
|
||||
|
||||
void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
||||
foreach (auto item, _renderItems.keys()) {
|
||||
foreach (auto item, _modelMeshRenderItems.keys()) {
|
||||
pendingChanges.removeItem(item);
|
||||
}
|
||||
_renderItems.clear();
|
||||
_renderItemsSet.clear();
|
||||
_modelMeshRenderItems.clear();
|
||||
_modelMeshRenderItemsSet.clear();
|
||||
foreach (auto item, _collisionRenderItems.keys()) {
|
||||
pendingChanges.removeItem(item);
|
||||
}
|
||||
_collisionRenderItems.clear();
|
||||
_collisionRenderItemsSet.clear();
|
||||
_meshGroupsKnown = false;
|
||||
_readyWhenAdded = false;
|
||||
}
|
||||
|
@ -1175,10 +1241,14 @@ void Model::segregateMeshGroups() {
|
|||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(_renderItems.isEmpty()); // We should not have any existing renderItems if we enter this section of code
|
||||
Q_ASSERT(_renderItemsSet.isEmpty()); // We should not have any existing renderItemsSet if we enter this section of code
|
||||
// We should not have any existing renderItems if we enter this section of code
|
||||
Q_ASSERT(_modelMeshRenderItems.isEmpty());
|
||||
Q_ASSERT(_modelMeshRenderItemsSet.isEmpty());
|
||||
Q_ASSERT(_collisionRenderItems.isEmpty());
|
||||
Q_ASSERT(_collisionRenderItemsSet.isEmpty());
|
||||
|
||||
_renderItemsSet.clear();
|
||||
_modelMeshRenderItemsSet.clear();
|
||||
_collisionRenderItemsSet.clear();
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(_translation);
|
||||
|
@ -1204,9 +1274,9 @@ void Model::segregateMeshGroups() {
|
|||
_collisionHullMaterial->setMetallic(0.02f);
|
||||
_collisionHullMaterial->setRoughness(0.5f);
|
||||
}
|
||||
_renderItemsSet << std::make_shared<MeshPartPayload>(networkMesh, partIndex, _collisionHullMaterial, transform, offset);
|
||||
_collisionRenderItemsSet << std::make_shared<MeshPartPayload>(networkMesh, partIndex, _collisionHullMaterial, transform, offset);
|
||||
} else {
|
||||
_renderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
|
||||
_modelMeshRenderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
|
||||
}
|
||||
|
||||
shapeID++;
|
||||
|
@ -1229,13 +1299,21 @@ bool Model::initWhenReady(render::ScenePointer scene) {
|
|||
offset.setScale(_scale);
|
||||
offset.postTranslate(_offset);
|
||||
|
||||
foreach (auto renderItem, _renderItemsSet) {
|
||||
foreach (auto renderItem, _modelMeshRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
|
||||
_modelMeshRenderItems.insert(item, renderPayload);
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
pendingChanges.updateItem<ModelMeshPartPayload>(item, [transform, offset](MeshPartPayload& data) {
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
}
|
||||
foreach (auto renderItem, _collisionRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||
_renderItems.insert(item, renderPayload);
|
||||
_collisionRenderItems.insert(item, renderPayload);
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
pendingChanges.updateItem<MeshPartPayload>(item, [transform,offset](MeshPartPayload& data) {
|
||||
data.updateTransform(transform, offset);
|
||||
pendingChanges.updateItem<MeshPartPayload>(item, [transform, offset](MeshPartPayload& data) {
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ namespace render {
|
|||
typedef unsigned int ItemID;
|
||||
}
|
||||
class MeshPartPayload;
|
||||
class ModelMeshPartPayload;
|
||||
class ModelRenderLocations;
|
||||
|
||||
inline uint qHash(const std::shared_ptr<MeshPartPayload>& a, uint seed) {
|
||||
|
@ -375,8 +376,12 @@ protected:
|
|||
bool _renderCollisionHull;
|
||||
|
||||
|
||||
QSet<std::shared_ptr<MeshPartPayload>> _renderItemsSet;
|
||||
QMap<render::ItemID, render::PayloadPointer> _renderItems;
|
||||
QSet<std::shared_ptr<MeshPartPayload>> _collisionRenderItemsSet;
|
||||
QMap<render::ItemID, render::PayloadPointer> _collisionRenderItems;
|
||||
|
||||
QSet<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItemsSet;
|
||||
QMap<render::ItemID, render::PayloadPointer> _modelMeshRenderItems;
|
||||
|
||||
bool _readyWhenAdded { false };
|
||||
bool _needsReload { true };
|
||||
bool _needsUpdateClusterMatrices { true };
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include "GeometryUtil.h"
|
||||
#include "NumericalConstants.h"
|
||||
|
||||
const glm::vec3 AABox::INFINITY_VECTOR(std::numeric_limits<float>::infinity());
|
||||
|
||||
AABox::AABox(const AACube& other) :
|
||||
_corner(other.getCorner()), _scale(other.getScale(), other.getScale(), other.getScale()) {
|
||||
}
|
||||
|
@ -34,7 +36,7 @@ AABox::AABox(const glm::vec3& corner, const glm::vec3& dimensions) :
|
|||
_corner(corner), _scale(dimensions) {
|
||||
};
|
||||
|
||||
AABox::AABox() : _corner(std::numeric_limits<float>::infinity()), _scale(0.0f) {
|
||||
AABox::AABox() : _corner(INFINITY_VECTOR), _scale(0.0f) {
|
||||
};
|
||||
|
||||
glm::vec3 AABox::calcCenter() const {
|
||||
|
@ -475,8 +477,15 @@ AABox AABox::clamp(float min, float max) const {
|
|||
}
|
||||
|
||||
AABox& AABox::operator += (const glm::vec3& point) {
|
||||
_corner = glm::min(_corner, point);
|
||||
_scale = glm::max(_scale, point - _corner);
|
||||
|
||||
if (isInvalid()) {
|
||||
_corner = glm::min(_corner, point);
|
||||
} else {
|
||||
glm::vec3 maximum(_corner + _scale);
|
||||
_corner = glm::min(_corner, point);
|
||||
maximum = glm::max(maximum, point);
|
||||
_scale = maximum - _corner;
|
||||
}
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
@ -484,17 +493,31 @@ AABox& AABox::operator += (const glm::vec3& point) {
|
|||
AABox& AABox::operator += (const AABox& box) {
|
||||
if (!box.isInvalid()) {
|
||||
(*this) += box._corner;
|
||||
_scale = glm::max(_scale, box.calcTopFarLeft() - _corner);
|
||||
(*this) += box.calcTopFarLeft();
|
||||
}
|
||||
return (*this);
|
||||
}
|
||||
|
||||
void AABox::embiggen(float scale) {
|
||||
_corner += scale * (-0.5f * _scale);
|
||||
_scale *= scale;
|
||||
}
|
||||
|
||||
void AABox::embiggen(const glm::vec3& scale) {
|
||||
_corner += scale * (-0.5f * _scale);
|
||||
_scale *= scale;
|
||||
}
|
||||
|
||||
void AABox::scale(float scale) {
|
||||
_corner *= scale;
|
||||
_scale *= scale;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -544,3 +567,47 @@ void AABox::transform(const Transform& transform) {
|
|||
rotate(transform.getRotation());
|
||||
translate(transform.getTranslation());
|
||||
}
|
||||
|
||||
void AABox::transform(const glm::mat4& matrix) {
|
||||
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 bottomLeftNearTransformed = transformPoint(matrix, bottomLeftNear);
|
||||
glm::vec3 bottomRightNearTransformed = transformPoint(matrix, bottomRightNear);
|
||||
glm::vec3 bottomLeftFarTransformed = transformPoint(matrix, bottomLeftFar);
|
||||
glm::vec3 bottomRightFarTransformed = transformPoint(matrix, bottomRightFar);
|
||||
glm::vec3 topLeftNearTransformed = transformPoint(matrix, topLeftNear);
|
||||
glm::vec3 topRightNearTransformed = transformPoint(matrix, topRightNear);
|
||||
glm::vec3 topLeftFarTransformed = transformPoint(matrix, topLeftFar);
|
||||
glm::vec3 topRightFarTransformed = transformPoint(matrix, topRightFar);
|
||||
|
||||
minimum = glm::min(bottomLeftNearTransformed,
|
||||
glm::min(bottomRightNearTransformed,
|
||||
glm::min(bottomLeftFarTransformed,
|
||||
glm::min(bottomRightFarTransformed,
|
||||
glm::min(topLeftNearTransformed,
|
||||
glm::min(topRightNearTransformed,
|
||||
glm::min(topLeftFarTransformed,
|
||||
topRightFarTransformed)))))));
|
||||
|
||||
maximum = glm::max(bottomLeftNearTransformed,
|
||||
glm::max(bottomRightNearTransformed,
|
||||
glm::max(bottomLeftFarTransformed,
|
||||
glm::max(bottomRightFarTransformed,
|
||||
glm::max(topLeftNearTransformed,
|
||||
glm::max(topRightNearTransformed,
|
||||
glm::max(topLeftFarTransformed,
|
||||
topRightFarTransformed)))))));
|
||||
|
||||
_corner = minimum;
|
||||
_scale = maximum - minimum;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,6 @@ public:
|
|||
const glm::vec3& getMinimumPoint() const { return _corner; }
|
||||
glm::vec3 getMaximumPoint() const { return calcTopFarLeft(); }
|
||||
|
||||
|
||||
bool contains(const glm::vec3& point) const;
|
||||
bool contains(const AABox& otherBox) const;
|
||||
bool touches(const AABox& otherBox) const;
|
||||
|
@ -93,10 +92,19 @@ public:
|
|||
void scale(float scale);
|
||||
void scale(const glm::vec3& scale);
|
||||
|
||||
/// make the AABox bigger (scale about it's center)
|
||||
void embiggen(float scale);
|
||||
void embiggen(const glm::vec3& scale);
|
||||
|
||||
// Transform the extents with transform
|
||||
void transform(const Transform& transform);
|
||||
|
||||
bool isInvalid() const { return _corner == glm::vec3(std::numeric_limits<float>::infinity()); }
|
||||
// Transform the extents with matrix
|
||||
void transform(const glm::mat4& matrix);
|
||||
|
||||
static const glm::vec3 INFINITY_VECTOR;
|
||||
|
||||
bool isInvalid() const { return _corner == INFINITY_VECTOR; }
|
||||
|
||||
private:
|
||||
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
||||
|
|
|
@ -151,3 +151,21 @@ void AABoxTests::testTouchesSphere() {
|
|||
}
|
||||
}
|
||||
|
||||
void AABoxTests::testScale() {
|
||||
AABox box1(glm::vec3(2.0f), glm::vec3(1.0f));
|
||||
QCOMPARE(box1.contains(glm::vec3(0.0f)), false);
|
||||
box1.scale(glm::vec3(10.0f));
|
||||
QCOMPARE(box1.contains(glm::vec3(0.0f)), false);
|
||||
QCOMPARE(box1.contains(glm::vec3(2.0f * 10.0f)), true);
|
||||
|
||||
AABox box2(glm::vec3(2.0f), glm::vec3(1.0f));
|
||||
QCOMPARE(box2.contains(glm::vec3(0.0f)), false);
|
||||
box2.embiggen(glm::vec3(10.0f));
|
||||
QCOMPARE(box2.contains(glm::vec3(0.0f)), true);
|
||||
|
||||
AABox box3;
|
||||
box3 += glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
box3 += glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
box3 += glm::vec3(-1.0f, -1.0f, -1.0f);
|
||||
QCOMPARE(box3.contains(glm::vec3(0.5f, 0.5f, 0.5f)), true);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ private slots:
|
|||
void testCtorsAndSetters();
|
||||
void testContainsPoint();
|
||||
void testTouchesSphere();
|
||||
void testScale();
|
||||
};
|
||||
|
||||
#endif // hifi_AABoxTests_h
|
||||
|
|
Loading…
Reference in a new issue