mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 15:29:42 +02: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.
|
// Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone.
|
||||||
// (Maybe this code should be moved there?)
|
// (Maybe this code should be moved there?)
|
||||||
// The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition().
|
// 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);
|
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; }
|
render::EnginePointer getRenderEngine() override { return _renderEngine; }
|
||||||
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
||||||
|
|
||||||
|
virtual void pushPreRenderLambda(void* key, std::function<void()> func) override;
|
||||||
|
|
||||||
const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
|
const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
|
||||||
|
|
||||||
void updateMyAvatarLookAtPosition();
|
void updateMyAvatarLookAtPosition();
|
||||||
|
@ -510,6 +512,9 @@ private:
|
||||||
bool _cursorNeedsChanging { false };
|
bool _cursorNeedsChanging { false };
|
||||||
|
|
||||||
QThread* _deadlockWatchdogThread;
|
QThread* _deadlockWatchdogThread;
|
||||||
|
|
||||||
|
std::map<void*, std::function<void()>> _preRenderLambdas;
|
||||||
|
std::mutex _preRenderLambdasLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Application_h
|
#endif // hifi_Application_h
|
||||||
|
|
|
@ -18,22 +18,22 @@
|
||||||
QString const ModelOverlay::TYPE = "model";
|
QString const ModelOverlay::TYPE = "model";
|
||||||
|
|
||||||
ModelOverlay::ModelOverlay()
|
ModelOverlay::ModelOverlay()
|
||||||
: _model(std::make_shared<Rig>()),
|
: _model(std::make_shared<Model>(std::make_shared<Rig>())),
|
||||||
_modelTextures(QVariantMap()),
|
_modelTextures(QVariantMap()),
|
||||||
_updateModel(false)
|
_updateModel(false)
|
||||||
{
|
{
|
||||||
_model.init();
|
_model->init();
|
||||||
_isLoaded = false;
|
_isLoaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
|
ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
|
||||||
Volume3DOverlay(modelOverlay),
|
Volume3DOverlay(modelOverlay),
|
||||||
_model(std::make_shared<Rig>()),
|
_model(std::make_shared<Model>(std::make_shared<Rig>())),
|
||||||
_modelTextures(QVariantMap()),
|
_modelTextures(QVariantMap()),
|
||||||
_url(modelOverlay->_url),
|
_url(modelOverlay->_url),
|
||||||
_updateModel(false)
|
_updateModel(false)
|
||||||
{
|
{
|
||||||
_model.init();
|
_model->init();
|
||||||
if (_url.isValid()) {
|
if (_url.isValid()) {
|
||||||
_updateModel = true;
|
_updateModel = true;
|
||||||
_isLoaded = false;
|
_isLoaded = false;
|
||||||
|
@ -44,27 +44,27 @@ void ModelOverlay::update(float deltatime) {
|
||||||
if (_updateModel) {
|
if (_updateModel) {
|
||||||
_updateModel = false;
|
_updateModel = false;
|
||||||
|
|
||||||
_model.setSnapModelToCenter(true);
|
_model->setSnapModelToCenter(true);
|
||||||
_model.setScale(getDimensions());
|
_model->setScale(getDimensions());
|
||||||
_model.setRotation(getRotation());
|
_model->setRotation(getRotation());
|
||||||
_model.setTranslation(getPosition());
|
_model->setTranslation(getPosition());
|
||||||
_model.setURL(_url);
|
_model->setURL(_url);
|
||||||
_model.simulate(deltatime, true);
|
_model->simulate(deltatime, true);
|
||||||
} else {
|
} 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) {
|
bool ModelOverlay::addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
||||||
Volume3DOverlay::addToScene(overlay, scene, pendingChanges);
|
Volume3DOverlay::addToScene(overlay, scene, pendingChanges);
|
||||||
_model.addToScene(scene, pendingChanges);
|
_model->addToScene(scene, pendingChanges);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelOverlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
void ModelOverlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
||||||
Volume3DOverlay::removeFromScene(overlay, scene, pendingChanges);
|
Volume3DOverlay::removeFromScene(overlay, scene, pendingChanges);
|
||||||
_model.removeFromScene(scene, pendingChanges);
|
_model->removeFromScene(scene, pendingChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelOverlay::render(RenderArgs* args) {
|
void ModelOverlay::render(RenderArgs* args) {
|
||||||
|
@ -73,9 +73,9 @@ void ModelOverlay::render(RenderArgs* args) {
|
||||||
// fix them up in the scene
|
// fix them up in the scene
|
||||||
render::ScenePointer scene = qApp->getMain3DScene();
|
render::ScenePointer scene = qApp->getMain3DScene();
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
if (_model.needsFixupInScene()) {
|
if (_model->needsFixupInScene()) {
|
||||||
_model.removeFromScene(scene, pendingChanges);
|
_model->removeFromScene(scene, pendingChanges);
|
||||||
_model.addToScene(scene, pendingChanges);
|
_model->addToScene(scene, pendingChanges);
|
||||||
}
|
}
|
||||||
scene->enqueuePendingChanges(pendingChanges);
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
|
||||||
if (newScale.x <= 0 || newScale.y <= 0 || newScale.z <= 0) {
|
if (newScale.x <= 0 || newScale.y <= 0 || newScale.z <= 0) {
|
||||||
setDimensions(scale);
|
setDimensions(scale);
|
||||||
} else {
|
} else {
|
||||||
_model.setScaleToFit(true, getDimensions());
|
_model->setScaleToFit(true, getDimensions());
|
||||||
_updateModel = true;
|
_updateModel = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
|
||||||
QUrl newTextureURL = textureMap[key].toUrl();
|
QUrl newTextureURL = textureMap[key].toUrl();
|
||||||
qDebug() << "Updating texture named" << key << "to texture at URL" << newTextureURL;
|
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 QString&, key),
|
||||||
Q_ARG(const QUrl&, newTextureURL));
|
Q_ARG(const QUrl&, newTextureURL));
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ QVariant ModelOverlay::getProperty(const QString& property) {
|
||||||
return _url.toString();
|
return _url.toString();
|
||||||
}
|
}
|
||||||
if (property == "dimensions" || property == "scale" || property == "size") {
|
if (property == "dimensions" || property == "scale" || property == "size") {
|
||||||
return vec3toVariant(_model.getScaleToFitDimensions());
|
return vec3toVariant(_model->getScaleToFitDimensions());
|
||||||
}
|
}
|
||||||
if (property == "textures") {
|
if (property == "textures") {
|
||||||
if (_modelTextures.size() > 0) {
|
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) {
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
|
||||||
|
|
||||||
QString subMeshNameTemp;
|
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,
|
bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QString& extraInfo) {
|
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 {
|
ModelOverlay* ModelOverlay::createClone() const {
|
||||||
|
|
|
@ -41,11 +41,11 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Model _model;
|
ModelPointer _model;
|
||||||
QVariantMap _modelTextures;
|
QVariantMap _modelTextures;
|
||||||
|
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
bool _updateModel;
|
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
|
// limit rotation
|
||||||
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
|
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
|
// directly set absolutePose rotation
|
||||||
_internalPoseSet._absolutePoses[index].rot = deltaQuat * headQuat;
|
_internalPoseSet._absolutePoses[index].rot = deltaQuat * headQuat;
|
||||||
|
|
|
@ -221,6 +221,8 @@ public:
|
||||||
|
|
||||||
void setEnableInverseKinematics(bool enable);
|
void setEnableInverseKinematics(bool enable);
|
||||||
|
|
||||||
|
const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
|
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
|
||||||
void updateAnimationStateHandlers();
|
void updateAnimationStateHandlers();
|
||||||
|
|
|
@ -111,7 +111,7 @@ Box Mesh::evalPartBound(int partNum) const {
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
Box Mesh::evalPartBounds(int partStart, int partEnd, Boxes& bounds) const {
|
Box Mesh::evalPartsBound(int partStart, int partEnd) const {
|
||||||
Box totalBound;
|
Box totalBound;
|
||||||
auto part = _partBuffer.cbegin<Part>() + partStart;
|
auto part = _partBuffer.cbegin<Part>() + partStart;
|
||||||
auto partItEnd = _partBuffer.cbegin<Part>() + partEnd;
|
auto partItEnd = _partBuffer.cbegin<Part>() + partEnd;
|
||||||
|
|
|
@ -108,9 +108,9 @@ public:
|
||||||
|
|
||||||
// evaluate the bounding box of A part
|
// evaluate the bounding box of A part
|
||||||
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
|
// evaluate the bounding boxes of the parts in the range [start, end]
|
||||||
// the returned box is the bounding box of ALL the evaluated part bounds.
|
// the returned box is the bounding box of ALL the evaluated parts bound.
|
||||||
Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const;
|
Box evalPartsBound(int partStart, int partEnd) const;
|
||||||
|
|
||||||
static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast<gpu::Primitive>(topo); }
|
static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast<gpu::Primitive>(topo); }
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,8 @@ public:
|
||||||
virtual render::ScenePointer getMain3DScene() = 0;
|
virtual render::ScenePointer getMain3DScene() = 0;
|
||||||
virtual render::EnginePointer getRenderEngine() = 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
|
// FIXME - we shouldn't assume that there's a single instance of an AbstractViewStateInterface
|
||||||
static AbstractViewStateInterface* instance();
|
static AbstractViewStateInterface* instance();
|
||||||
static void setInstance(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();
|
auto vertexFormat = _drawMesh->getVertexFormat();
|
||||||
_hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR);
|
_hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR);
|
||||||
_drawPart = _drawMesh->getPartBuffer().get<model::Mesh::Part>(partIndex);
|
_drawPart = _drawMesh->getPartBuffer().get<model::Mesh::Part>(partIndex);
|
||||||
|
|
||||||
_localBound = _drawMesh->evalPartBound(partIndex);
|
_localBound = _drawMesh->evalPartBound(partIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -352,7 +351,23 @@ void ModelMeshPartPayload::initCache() {
|
||||||
|
|
||||||
|
|
||||||
void ModelMeshPartPayload::notifyLocationChanged() {
|
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 {
|
ItemKey ModelMeshPartPayload::getKey() const {
|
||||||
|
@ -377,12 +392,6 @@ ItemKey ModelMeshPartPayload::getKey() const {
|
||||||
return builder.build();
|
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 {
|
ShapeKey ModelMeshPartPayload::getShapeKey() const {
|
||||||
assert(_model->isLoaded());
|
assert(_model->isLoaded());
|
||||||
const FBXGeometry& geometry = _model->getFBXGeometry();
|
const FBXGeometry& geometry = _model->getFBXGeometry();
|
||||||
|
|
|
@ -74,15 +74,15 @@ namespace render {
|
||||||
class ModelMeshPartPayload : public MeshPartPayload {
|
class ModelMeshPartPayload : public MeshPartPayload {
|
||||||
public:
|
public:
|
||||||
ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
|
ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
|
||||||
|
|
||||||
typedef render::Payload<ModelMeshPartPayload> Payload;
|
typedef render::Payload<ModelMeshPartPayload> Payload;
|
||||||
typedef Payload::DataPointer Pointer;
|
typedef Payload::DataPointer Pointer;
|
||||||
|
|
||||||
void notifyLocationChanged() override;
|
void notifyLocationChanged() override;
|
||||||
|
void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector<glm::mat4>& clusterMatrices);
|
||||||
|
|
||||||
// Render Item interface
|
// Render Item interface
|
||||||
render::ItemKey getKey() const override;
|
render::ItemKey getKey() const override;
|
||||||
render::Item::Bound getBound() const override;
|
|
||||||
render::ShapeKey getShapeKey() const override; // shape interface
|
render::ShapeKey getShapeKey() const override; // shape interface
|
||||||
void render(RenderArgs* args) const override;
|
void render(RenderArgs* args) const override;
|
||||||
|
|
||||||
|
@ -101,4 +101,11 @@ public:
|
||||||
bool _isBlendShaped{ false };
|
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
|
#endif // hifi_MeshPartPayload_h
|
||||||
|
|
|
@ -130,25 +130,60 @@ void Model::setOffset(const glm::vec3& offset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::enqueueLocationChange() {
|
void Model::enqueueLocationChange() {
|
||||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
|
||||||
|
|
||||||
Transform transform;
|
// queue up this work for later processing, at the end of update and just before rendering.
|
||||||
transform.setTranslation(_translation);
|
// the application will ensure only the last lambda is actually invoked.
|
||||||
transform.setRotation(_rotation);
|
void* key = (void*)this;
|
||||||
|
std::weak_ptr<Model> weakSelf = shared_from_this();
|
||||||
|
AbstractViewStateInterface::instance()->pushPreRenderLambda(key, [weakSelf]() {
|
||||||
|
|
||||||
Transform offset;
|
// do nothing, if the model has already been destroyed.
|
||||||
offset.setScale(_scale);
|
auto self = weakSelf.lock();
|
||||||
offset.postTranslate(_offset);
|
if (!self) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
render::PendingChanges pendingChanges;
|
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||||
foreach (auto itemID, _renderItems.keys()) {
|
|
||||||
pendingChanges.updateItem<MeshPartPayload>(itemID, [transform, offset](MeshPartPayload& data) {
|
|
||||||
data.updateTransform(transform, offset);
|
|
||||||
data.notifyLocationChanged();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
void Model::initJointTransforms() {
|
||||||
|
@ -497,8 +532,11 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scen
|
||||||
_isVisible = newValue;
|
_isVisible = newValue;
|
||||||
|
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
foreach (auto item, _renderItems.keys()) {
|
foreach (auto item, _modelMeshRenderItems.keys()) {
|
||||||
pendingChanges.resetItem(item, _renderItems[item]);
|
pendingChanges.resetItem(item, _modelMeshRenderItems[item]);
|
||||||
|
}
|
||||||
|
foreach (auto item, _collisionRenderItems.keys()) {
|
||||||
|
pendingChanges.resetItem(item, _modelMeshRenderItems[item]);
|
||||||
}
|
}
|
||||||
scene->enqueuePendingChanges(pendingChanges);
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
}
|
}
|
||||||
|
@ -514,14 +552,25 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChan
|
||||||
|
|
||||||
bool somethingAdded = false;
|
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 item = scene->allocateID();
|
||||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||||
pendingChanges.resetItem(item, renderPayload);
|
pendingChanges.resetItem(item, renderPayload);
|
||||||
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
|
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
|
||||||
data.notifyLocationChanged();
|
data.notifyLocationChanged();
|
||||||
});
|
});
|
||||||
_renderItems.insert(item, renderPayload);
|
_collisionRenderItems.insert(item, renderPayload);
|
||||||
somethingAdded = true;
|
somethingAdded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,7 +590,19 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
||||||
|
|
||||||
bool somethingAdded = false;
|
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 item = scene->allocateID();
|
||||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||||
renderPayload->addStatusGetters(statusGetters);
|
renderPayload->addStatusGetters(statusGetters);
|
||||||
|
@ -549,7 +610,7 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
||||||
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
|
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
|
||||||
data.notifyLocationChanged();
|
data.notifyLocationChanged();
|
||||||
});
|
});
|
||||||
_renderItems.insert(item, renderPayload);
|
_collisionRenderItems.insert(item, renderPayload);
|
||||||
somethingAdded = true;
|
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) {
|
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);
|
pendingChanges.removeItem(item);
|
||||||
}
|
}
|
||||||
_renderItems.clear();
|
_modelMeshRenderItems.clear();
|
||||||
_renderItemsSet.clear();
|
_modelMeshRenderItemsSet.clear();
|
||||||
|
foreach (auto item, _collisionRenderItems.keys()) {
|
||||||
|
pendingChanges.removeItem(item);
|
||||||
|
}
|
||||||
|
_collisionRenderItems.clear();
|
||||||
|
_collisionRenderItemsSet.clear();
|
||||||
_meshGroupsKnown = false;
|
_meshGroupsKnown = false;
|
||||||
_readyWhenAdded = false;
|
_readyWhenAdded = false;
|
||||||
}
|
}
|
||||||
|
@ -1175,10 +1241,14 @@ void Model::segregateMeshGroups() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_ASSERT(_renderItems.isEmpty()); // We should not have any existing renderItems if we enter this section of code
|
// 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
|
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 transform;
|
||||||
transform.setTranslation(_translation);
|
transform.setTranslation(_translation);
|
||||||
|
@ -1204,9 +1274,9 @@ void Model::segregateMeshGroups() {
|
||||||
_collisionHullMaterial->setMetallic(0.02f);
|
_collisionHullMaterial->setMetallic(0.02f);
|
||||||
_collisionHullMaterial->setRoughness(0.5f);
|
_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 {
|
} else {
|
||||||
_renderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
|
_modelMeshRenderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
shapeID++;
|
shapeID++;
|
||||||
|
@ -1229,13 +1299,21 @@ bool Model::initWhenReady(render::ScenePointer scene) {
|
||||||
offset.setScale(_scale);
|
offset.setScale(_scale);
|
||||||
offset.postTranslate(_offset);
|
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 item = scene->allocateID();
|
||||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||||
_renderItems.insert(item, renderPayload);
|
_collisionRenderItems.insert(item, renderPayload);
|
||||||
pendingChanges.resetItem(item, renderPayload);
|
pendingChanges.resetItem(item, renderPayload);
|
||||||
pendingChanges.updateItem<MeshPartPayload>(item, [transform,offset](MeshPartPayload& data) {
|
pendingChanges.updateItem<MeshPartPayload>(item, [transform, offset](MeshPartPayload& data) {
|
||||||
data.updateTransform(transform, offset);
|
|
||||||
data.notifyLocationChanged();
|
data.notifyLocationChanged();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ namespace render {
|
||||||
typedef unsigned int ItemID;
|
typedef unsigned int ItemID;
|
||||||
}
|
}
|
||||||
class MeshPartPayload;
|
class MeshPartPayload;
|
||||||
|
class ModelMeshPartPayload;
|
||||||
class ModelRenderLocations;
|
class ModelRenderLocations;
|
||||||
|
|
||||||
inline uint qHash(const std::shared_ptr<MeshPartPayload>& a, uint seed) {
|
inline uint qHash(const std::shared_ptr<MeshPartPayload>& a, uint seed) {
|
||||||
|
@ -375,8 +376,12 @@ protected:
|
||||||
bool _renderCollisionHull;
|
bool _renderCollisionHull;
|
||||||
|
|
||||||
|
|
||||||
QSet<std::shared_ptr<MeshPartPayload>> _renderItemsSet;
|
QSet<std::shared_ptr<MeshPartPayload>> _collisionRenderItemsSet;
|
||||||
QMap<render::ItemID, render::PayloadPointer> _renderItems;
|
QMap<render::ItemID, render::PayloadPointer> _collisionRenderItems;
|
||||||
|
|
||||||
|
QSet<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItemsSet;
|
||||||
|
QMap<render::ItemID, render::PayloadPointer> _modelMeshRenderItems;
|
||||||
|
|
||||||
bool _readyWhenAdded { false };
|
bool _readyWhenAdded { false };
|
||||||
bool _needsReload { true };
|
bool _needsReload { true };
|
||||||
bool _needsUpdateClusterMatrices { true };
|
bool _needsUpdateClusterMatrices { true };
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include "GeometryUtil.h"
|
#include "GeometryUtil.h"
|
||||||
#include "NumericalConstants.h"
|
#include "NumericalConstants.h"
|
||||||
|
|
||||||
|
const glm::vec3 AABox::INFINITY_VECTOR(std::numeric_limits<float>::infinity());
|
||||||
|
|
||||||
AABox::AABox(const AACube& other) :
|
AABox::AABox(const AACube& other) :
|
||||||
_corner(other.getCorner()), _scale(other.getScale(), other.getScale(), other.getScale()) {
|
_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) {
|
_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 {
|
glm::vec3 AABox::calcCenter() const {
|
||||||
|
@ -475,8 +477,15 @@ AABox AABox::clamp(float min, float max) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
AABox& AABox::operator += (const glm::vec3& point) {
|
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);
|
return (*this);
|
||||||
}
|
}
|
||||||
|
@ -484,17 +493,31 @@ AABox& AABox::operator += (const glm::vec3& point) {
|
||||||
AABox& AABox::operator += (const AABox& box) {
|
AABox& AABox::operator += (const AABox& box) {
|
||||||
if (!box.isInvalid()) {
|
if (!box.isInvalid()) {
|
||||||
(*this) += box._corner;
|
(*this) += box._corner;
|
||||||
_scale = glm::max(_scale, box.calcTopFarLeft() - _corner);
|
(*this) += box.calcTopFarLeft();
|
||||||
}
|
}
|
||||||
return (*this);
|
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) {
|
void AABox::scale(const glm::vec3& scale) {
|
||||||
_corner *= scale;
|
_corner *= scale;
|
||||||
_scale *= scale;
|
_scale *= scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AABox::rotate(const glm::quat& rotation) {
|
void AABox::rotate(const glm::quat& rotation) {
|
||||||
auto minimum = _corner;
|
auto minimum = _corner;
|
||||||
auto maximum = _corner + _scale;
|
auto maximum = _corner + _scale;
|
||||||
|
@ -544,3 +567,47 @@ void AABox::transform(const Transform& transform) {
|
||||||
rotate(transform.getRotation());
|
rotate(transform.getRotation());
|
||||||
translate(transform.getTranslation());
|
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; }
|
const glm::vec3& getMinimumPoint() const { return _corner; }
|
||||||
glm::vec3 getMaximumPoint() const { return calcTopFarLeft(); }
|
glm::vec3 getMaximumPoint() const { return calcTopFarLeft(); }
|
||||||
|
|
||||||
|
|
||||||
bool contains(const glm::vec3& point) const;
|
bool contains(const glm::vec3& point) const;
|
||||||
bool contains(const AABox& otherBox) const;
|
bool contains(const AABox& otherBox) const;
|
||||||
bool touches(const AABox& otherBox) const;
|
bool touches(const AABox& otherBox) const;
|
||||||
|
@ -93,10 +92,19 @@ public:
|
||||||
void scale(float scale);
|
void scale(float scale);
|
||||||
void scale(const glm::vec3& 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
|
// Transform the extents with transform
|
||||||
void transform(const Transform& 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:
|
private:
|
||||||
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
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 testCtorsAndSetters();
|
||||||
void testContainsPoint();
|
void testContainsPoint();
|
||||||
void testTouchesSphere();
|
void testTouchesSphere();
|
||||||
|
void testScale();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AABoxTests_h
|
#endif // hifi_AABoxTests_h
|
||||||
|
|
Loading…
Reference in a new issue