Merge pull request #4 from sethalves/rig

Rig
This commit is contained in:
Howard Stearns 2015-07-23 16:32:56 -05:00
commit 9f8fad0b8f
16 changed files with 257 additions and 236 deletions

View file

@ -1175,13 +1175,22 @@ void OctreeServer::aboutToFinish() {
if (_jurisdictionSender) {
_jurisdictionSender->terminating();
}
QSet<SharedNodePointer> nodesToShutdown;
// force a shutdown of all of our OctreeSendThreads - at this point it has to be impossible for a
// linkedDataCreateCallback to be called for a new node
nodeList->eachNode([this](const SharedNodePointer& node) {
// Force a shutdown of all of our OctreeSendThreads.
// At this point it has to be impossible for a linkedDataCreateCallback to be called for a new node
nodeList->eachNode([&nodesToShutdown](const SharedNodePointer& node) {
nodesToShutdown << node;
});
// What follows is a hack to force OctreeSendThreads to cleanup before the OctreeServer is gone.
// I would prefer to allow the SharedNodePointer ref count drop to zero to do this automatically
// but that isn't possible as long as the OctreeSendThread has an OctreeServer* that it uses.
for (auto& node : nodesToShutdown) {
qDebug() << qPrintable(_safeServerName) << "server about to finish while node still connected node:" << *node;
forceNodeShutdown(node);
});
}
if (_persistThread) {
_persistThread->aboutToFinish();

View file

@ -318,11 +318,15 @@ var toolBar = (function () {
print("Resize failed: timed out waiting for model (" + url + ") to load");
}
} else {
entityProperties.dimensions = naturalDimensions;
Entities.editEntity(entityId, entityProperties);
Entities.editEntity(entityId, { dimensions: naturalDimensions });
// Reset selection so that the selection overlays will be updated
selectionManager.setSelections([entityId]);
}
}
selectionManager.setSelections([entityId]);
Script.setTimeout(resize, RESIZE_INTERVAL);
} else {
print("Can't add model: Model would be out of bounds.");

View file

@ -882,7 +882,7 @@ void Application::paintGL() {
// NOTE: There is no batch associated with this renderArgs
// the ApplicationOverlay class assumes it's viewport is setup to be the device size
QSize size = qApp->getDeviceSize();
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
_applicationOverlay.renderOverlay(&renderArgs);
}
@ -959,7 +959,7 @@ void Application::paintGL() {
batch.setFramebuffer(primaryFbo);
// clear the normal and specular buffers
batch.clearFramebuffer(
gpu::Framebuffer::BUFFER_COLOR0 |
gpu::Framebuffer::BUFFER_COLOR0 |
gpu::Framebuffer::BUFFER_COLOR1 |
gpu::Framebuffer::BUFFER_COLOR2 |
gpu::Framebuffer::BUFFER_DEPTH,
@ -1798,6 +1798,13 @@ void Application::idle() {
}
double timeSinceLastUpdate = (double)_lastTimeUpdated.nsecsElapsed() / 1000000.0;
if (timeSinceLastUpdate > targetFramePeriod) {
{
static const int IDLE_EVENT_PROCESS_MAX_TIME_MS = 2;
PerformanceTimer perfTimer("processEvents");
processEvents(QEventLoop::AllEvents, IDLE_EVENT_PROCESS_MAX_TIME_MS);
}
_lastTimeUpdated.start();
{
PerformanceTimer perfTimer("update");
@ -1822,11 +1829,19 @@ void Application::idle() {
_idleLoopMeasuredJitter = _idleLoopStdev.getStDev();
_idleLoopStdev.reset();
}
}
// After finishing all of the above work, ensure the idle timer is set to the proper interval,
// depending on whether we're throttling or not
idleTimer->start(_glWidget->isThrottleRendering() ? THROTTLED_IDLE_TIMER_DELAY : 1);
// depending on whether we're throttling or not.
// Once rendering is off on another thread we should be able to have Application::idle run at start(0) in
// perpetuity and not expect events to get backed up.
static const int IDLE_TIMER_DELAY_MS = 0;
int desiredInterval = _glWidget->isThrottleRendering() ? THROTTLED_IDLE_TIMER_DELAY : IDLE_TIMER_DELAY_MS;
if (idleTimer->interval() != desiredInterval) {
idleTimer->start(desiredInterval);
}
}
// check for any requested background downloads.
@ -3414,9 +3429,6 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi
renderArgs->_context->render(batch);
}
bool updateViewFrustum = false;
loadViewFrustum(_mirrorCamera, _viewFrustum);
// render rear mirror view
displaySide(renderArgs, _mirrorCamera, true, billboard);

View file

@ -317,15 +317,14 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::S
}
}
void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, bool postLighting) {
void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
if (_referential) {
_referential->update();
}
auto& batch = *renderArgs->_batch;
if (postLighting &&
glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), _position) < 10.0f) {
if (glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), _position) < 10.0f) {
auto geometryCache = DependencyManager::get<GeometryCache>();
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
@ -415,9 +414,9 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
: GLOW_FROM_AVERAGE_LOUDNESS;
// render body
renderBody(renderArgs, frustum, postLighting, glowLevel);
renderBody(renderArgs, frustum, glowLevel);
if (!postLighting && renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE) {
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE) {
// add local lights
const float BASE_LIGHT_DISTANCE = 2.0f;
const float LIGHT_EXPONENT = 1.0f;
@ -432,21 +431,17 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
}
}
if (postLighting) {
bool renderSkeleton = Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes);
bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes);
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
if (renderSkeleton) {
_skeletonModel.renderJointCollisionShapes(0.7f);
}
if (renderHead && shouldRenderHead(renderArgs)) {
getHead()->getFaceModel().renderJointCollisionShapes(0.7f);
}
if (renderBounding && shouldRenderHead(renderArgs)) {
_skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f);
}
bool renderSkeleton = Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes);
bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes);
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
if (renderSkeleton) {
_skeletonModel.renderJointCollisionShapes(0.7f);
}
if (renderHead && shouldRenderHead(renderArgs)) {
getHead()->getFaceModel().renderJointCollisionShapes(0.7f);
}
if (renderBounding && shouldRenderHead(renderArgs)) {
_skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f);
}
// Stack indicator spheres
@ -570,24 +565,20 @@ void Avatar::fixupModelsInScene() {
scene->enqueuePendingChanges(pendingChanges);
}
void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel) {
void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel) {
fixupModelsInScene();
{
if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
if (postLighting || renderArgs->_renderMode == RenderArgs::SHADOW_RENDER_MODE) {
// render the billboard until both models are loaded
renderBillboard(renderArgs);
}
// render the billboard until both models are loaded
renderBillboard(renderArgs);
return;
}
if (postLighting) {
getHand()->render(renderArgs, false);
}
getHand()->render(renderArgs, false);
}
getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting);
getHead()->render(renderArgs, 1.0f, renderFrustum);
}
bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
@ -832,7 +823,7 @@ QVector<glm::quat> Avatar::getJointRotations() const {
}
QVector<glm::quat> jointRotations(_skeletonModel.getJointStateCount());
for (int i = 0; i < _skeletonModel.getJointStateCount(); ++i) {
_skeletonModel.getJointState(i, jointRotations[i]);
_skeletonModel.getJointRotation(i, jointRotations[i]);
}
return jointRotations;
}
@ -842,7 +833,7 @@ glm::quat Avatar::getJointRotation(int index) const {
return AvatarData::getJointRotation(index);
}
glm::quat rotation;
_skeletonModel.getJointState(index, rotation);
_skeletonModel.getJointRotation(index, rotation);
return rotation;
}

View file

@ -81,8 +81,7 @@ public:
void init();
void simulate(float deltaTime);
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition,
bool postLighting = false);
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
bool addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges);
@ -235,7 +234,7 @@ protected:
Transform calculateDisplayNameTransform(const ViewFrustum& frustum, float fontSize, const glm::ivec4& viewport) const;
void renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, const glm::ivec4& viewport) const;
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f);
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f);
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const;
virtual void fixupModelsInScene();

View file

@ -293,7 +293,7 @@ void Head::relaxLean(float deltaTime) {
_deltaLeanForward *= relaxationFactor;
}
void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum, bool postLighting) {
void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum) {
if (_renderLookatVectors) {
renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition());
}

View file

@ -33,7 +33,7 @@ public:
void init();
void reset();
void simulate(float deltaTime, bool isMine, bool billboard = false);
void render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum, bool postLighting);
void render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum);
void setScale(float scale);
void setPosition(glm::vec3 position) { _position = position; }
void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }

View file

@ -216,10 +216,10 @@ void MyAvatar::simulate(float deltaTime) {
{
PerformanceTimer perfTimer("joints");
// copy out the skeleton joints from the model
_jointData.resize(_skeletonModel.getJointStateCount());
_jointData.resize(_rig->getJointStateCount());
for (int i = 0; i < _jointData.size(); i++) {
JointData& data = _jointData[i];
data.valid = _skeletonModel.getJointState(i, data.rotation);
data.valid = _rig->getJointStateRotation(i, data.rotation);
}
}
@ -333,13 +333,13 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
// virtual
void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, bool postLighting) {
void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
// don't render if we've been asked to disable local rendering
if (!_shouldRender) {
return; // exit early
}
Avatar::render(renderArgs, cameraPosition, postLighting);
Avatar::render(renderArgs, cameraPosition);
// don't display IK constraints in shadow mode
if (Menu::getInstance()->isOptionChecked(MenuOption::ShowIKConstraints) &&
@ -1227,7 +1227,7 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, const g
Avatar::attach(modelURL, jointName, translation, rotation, scale, allowDuplicates, useSaved);
}
void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel) {
void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel) {
if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
return; // wait until all models are loaded
@ -1237,7 +1237,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bo
// Render head so long as the camera isn't inside it
if (shouldRenderHead(renderArgs)) {
getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting);
getHead()->render(renderArgs, 1.0f, renderFrustum);
}
getHand()->render(renderArgs, true);
}

View file

@ -46,8 +46,8 @@ public:
void preRender(RenderArgs* renderArgs);
void updateFromTrackers(float deltaTime);
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, bool postLighting = false) override;
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f) override;
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override;
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
// setters

View file

@ -13,6 +13,7 @@
#include <QMultiMap>
#include <CapsuleShape.h>
#include <DeferredLightingEffect.h>
#include <SphereShape.h>
#include "Application.h"
@ -787,31 +788,34 @@ void SkeletonModel::resetShapePositionsToDefaultPose() {
void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) {
const int BALL_SUBDIVISIONS = 10;
if (_shapes.isEmpty()) {
// the bounding shape has not been propery computed
// the bounding shape has not been properly computed
// so no need to render it
return;
}
// draw a blue sphere at the capsule endpoint
auto geometryCache = DependencyManager::get<GeometryCache>();
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
Transform transform; // = Transform();
// draw a blue sphere at the capsule end point
glm::vec3 endPoint;
_boundingShape.getEndPoint(endPoint);
endPoint = endPoint + _translation;
Transform transform = Transform();
transform.setTranslation(endPoint);
batch.setModelTransform(transform);
auto geometryCache = DependencyManager::get<GeometryCache>();
geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
deferredLighting->bindSimpleProgram(batch);
geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
glm::vec4(0.6f, 0.6f, 0.8f, alpha));
// draw a yellow sphere at the capsule startpoint
// draw a yellow sphere at the capsule start point
glm::vec3 startPoint;
_boundingShape.getStartPoint(startPoint);
startPoint = startPoint + _translation;
glm::vec3 axis = endPoint - startPoint;
Transform axisTransform = Transform();
axisTransform.setTranslation(-axis);
batch.setModelTransform(axisTransform);
geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
transform.setTranslation(startPoint);
batch.setModelTransform(transform);
deferredLighting->bindSimpleProgram(batch);
geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
glm::vec4(0.8f, 0.8f, 0.6f, alpha));
// draw a green cylinder between the two points

View file

@ -91,6 +91,7 @@ void BillboardOverlay::render(RenderArgs* args) {
if (batch) {
Transform transform = _transform;
transform.postScale(glm::vec3(getDimensions(), 1.0f));
transform.setRotation(rotation);
batch->setModelTransform(transform);
batch->setResourceTexture(0, _texture->getGPUTexture());

View file

@ -41,6 +41,14 @@ bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) {
return _runningAnimations.contains(animationHandle);
}
void Rig::deleteAnimations() {
for (QSet<AnimationHandlePointer>::iterator it = _animationHandles.begin(); it != _animationHandles.end(); ) {
(*it)->clearJoints();
it = _animationHandles.erase(it);
}
}
float Rig::initJointStates(QVector<JointState> states, glm::mat4 parentTransform) {
_jointStates = states;
initJointTransforms(parentTransform);
@ -235,7 +243,12 @@ glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const {
return _jointStates[jointIndex].getVisibleTransform();
}
void Rig::simulateInternal(glm::mat4 parentTransform) {
void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform) {
// update animations
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
handle->simulate(deltaTime);
}
for (int i = 0; i < _jointStates.size(); i++) {
updateJointState(i, parentTransform);
}

View file

@ -49,6 +49,7 @@ public:
void addRunningAnimation(AnimationHandlePointer animationHandle);
bool isRunningAnimation(AnimationHandlePointer animationHandle);
const QList<AnimationHandlePointer>& getRunningAnimations() const { return _runningAnimations; }
void deleteAnimations();
float initJointStates(QVector<JointState> states, glm::mat4 parentTransform);
bool jointStatesEmpty() { return _jointStates.isEmpty(); };
@ -81,7 +82,7 @@ public:
void setJointTransform(int jointIndex, glm::mat4 newTransform);
glm::mat4 getJointVisibleTransform(int jointIndex) const;
void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform);
void simulateInternal(glm::mat4 parentTransform);
void simulateInternal(float deltaTime, glm::mat4 parentTransform);
bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority,
const QVector<int>& freeLineage, glm::mat4 parentTransform);

View file

@ -43,9 +43,18 @@ btConvexHullShape* ShapeFactory::createConvexHull(const QVector<glm::vec3>& poin
const float MIN_MARGIN = 0.01f;
glm::vec3 diagonal = maxCorner - minCorner;
float minDimension = glm::min(diagonal[0], diagonal[1]);
minDimension = glm::min(minDimension, diagonal[2]);
margin = glm::min(glm::max(0.5f * minDimension, MIN_MARGIN), margin);
float smallestDimension = glm::min(diagonal[0], diagonal[1]);
smallestDimension = glm::min(smallestDimension, diagonal[2]);
const float MIN_DIMENSION = 2.0f * MIN_MARGIN + 0.001f;
if (smallestDimension < MIN_DIMENSION) {
for (int i = 0; i < 3; ++i) {
if (diagonal[i] < MIN_DIMENSION) {
diagonal[i] = MIN_DIMENSION;
}
}
smallestDimension = MIN_DIMENSION;
}
margin = glm::min(glm::max(0.5f * smallestDimension, MIN_MARGIN), margin);
hull->setMargin(margin);
// add the points, correcting for margin

View file

@ -1365,18 +1365,13 @@ void Model::updateClusterMatrices() {
void Model::simulateInternal(float deltaTime) {
// update the world space transforms for all joints
// update animations
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
handle->simulate(deltaTime);
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
_rig->simulateInternal(parentTransform);
_rig->simulateInternal(deltaTime, parentTransform);
_shapesAreDirty = !_shapes.isEmpty();
glm::mat4 modelToWorld = glm::mat4_cast(_rotation);
for (int i = 0; i < _meshStates.size(); i++) {
MeshState& state = _meshStates[i];
@ -1512,21 +1507,13 @@ void Model::deleteGeometry() {
_rig->clearJointStates();
_meshStates.clear();
clearShapes();
for (QSet<WeakAnimationHandlePointer>::iterator it = _animationHandles.begin(); it != _animationHandles.end(); ) {
AnimationHandlePointer handle = it->lock();
if (handle) {
handle->clearJoints();
it++;
} else {
it = _animationHandles.erase(it);
}
}
_rig->deleteAnimations();
if (_geometry) {
_geometry->clearLoadPriority(this);
}
_blendedBlendshapeCoefficients.clear();
}

View file

@ -57,7 +57,7 @@ inline uint qHash(const std::shared_ptr<MeshPartPayload>& a, uint seed) {
/// A generic 3D model displaying geometry loaded from a URL.
class Model : public QObject, public PhysicsEntity {
Q_OBJECT
public:
typedef RenderArgs::RenderMode RenderMode;
@ -66,58 +66,7 @@ public:
Model(RigPointer rig, QObject* parent = nullptr);
virtual ~Model();
/// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension
void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false);
bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled
bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit
const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to
void setScaleToFit(bool scaleToFit, const glm::vec3& dimensions);
void setSnapModelToCenter(bool snapModelToCenter) {
setSnapModelToRegistrationPoint(snapModelToCenter, glm::vec3(0.5f,0.5f,0.5f));
};
bool getSnapModelToCenter() {
return _snapModelToRegistrationPoint && _registrationPoint == glm::vec3(0.5f,0.5f,0.5f);
}
void setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint);
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
void setScale(const glm::vec3& scale);
const glm::vec3& getScale() const { return _scale; }
void setOffset(const glm::vec3& offset);
const glm::vec3& getOffset() const { return _offset; }
void setPupilDilation(float dilation) { _pupilDilation = dilation; }
float getPupilDilation() const { return _pupilDilation; }
void setBlendshapeCoefficients(const QVector<float>& coefficients) { _blendshapeCoefficients = coefficients; }
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
bool isActive() const { return _geometry && _geometry->isLoaded(); }
bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().isEmpty()); }
void setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scene);
bool isVisible() const { return _isVisible; }
bool isLoaded() const { return _geometry && _geometry->isLoaded(); }
bool isLoadedWithTextures() const { return _geometry && _geometry->isLoadedWithTextures(); }
void init();
void reset();
virtual void simulate(float deltaTime, bool fullUpdate = true);
void renderSetup(RenderArgs* args);
// new Scene/Engine rendering support
bool needsFixupInScene() { return !_readyWhenAdded && readyToAddToScene(); }
bool readyToAddToScene(RenderArgs* renderArgs = nullptr) { return !_needsReload && isRenderable() && isActive() && isLoaded(); }
bool addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
bool addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters);
void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
/// Sets the URL of the model to render.
/// \param fallback the URL of a fallback model to render if the requested model fails to load
@ -127,22 +76,127 @@ public:
bool retainCurrent = false, bool delayLoad = false);
const QUrl& getURL() const { return _url; }
// new Scene/Engine rendering support
void setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scene);
bool needsFixupInScene() { return !_readyWhenAdded && readyToAddToScene(); }
bool readyToAddToScene(RenderArgs* renderArgs = nullptr) {
return !_needsReload && isRenderable() && isActive() && isLoaded();
}
bool initWhenReady(render::ScenePointer scene);
bool addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
bool addToScene(std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges,
render::Item::Status::Getters& statusGetters);
void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
void renderSetup(RenderArgs* args);
bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().isEmpty()); }
virtual void renderJointCollisionShapes(float alpha);
bool isVisible() const { return _isVisible; }
AABox getPartBounds(int meshIndex, int partIndex);
void renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent);
bool maybeStartBlender();
/// Sets blended vertices computed in a separate thread.
void setBlendedVertices(int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
bool isLoaded() const { return _geometry && _geometry->isLoaded(); }
bool isLoadedWithTextures() const { return _geometry && _geometry->isLoadedWithTextures(); }
void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; }
bool isWireframe() const { return _isWireframe; }
void init();
void reset();
void setScaleToFit(bool scaleToFit, const glm::vec3& dimensions);
void setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint);
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
virtual void simulate(float deltaTime, bool fullUpdate = true);
/// Returns a reference to the shared geometry.
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
bool isActive() const { return _geometry && _geometry->isLoaded(); }
Q_INVOKABLE void setTextureWithNameToURL(const QString& name, const QUrl& url)
{ _geometry->setTextureWithNameToURL(name, url); }
bool convexHullContains(glm::vec3 point);
QStringList getJointNames() const;
/// Sets the joint state at the specified index.
void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f);
bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, QString& extraInfo, bool pickAgainstTriangles = false);
// Set the model to use for collisions
Q_INVOKABLE void setCollisionModelURL(const QUrl& url);
const QUrl& getCollisionURL() const { return _collisionUrl; }
void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; }
bool isWireframe() const { return _isWireframe; }
/// Returns a reference to the shared collision geometry.
const QSharedPointer<NetworkGeometry> getCollisionGeometry(bool delayLoad = true);
void setOffset(const glm::vec3& offset);
const glm::vec3& getOffset() const { return _offset; }
/// Sets the distance parameter used for LOD computations.
void setLODDistance(float distance) { _lodDistance = distance; }
const QList<AnimationHandlePointer>& getRunningAnimations() const { return _rig->getRunningAnimations(); }
/// Clear the joint animation priority
void clearJointAnimationPriority(int index);
void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false);
bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled
void setSnapModelToCenter(bool snapModelToCenter) {
setSnapModelToRegistrationPoint(snapModelToCenter, glm::vec3(0.5f,0.5f,0.5f));
};
bool getSnapModelToCenter() {
return _snapModelToRegistrationPoint && _registrationPoint == glm::vec3(0.5f,0.5f,0.5f);
}
/// Returns the number of joint states in the model.
int getJointStateCount() const { return _rig->getJointStateCount(); }
bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const;
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;
bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const;
/// \param jointIndex index of joint in model structure
/// \param rotation[out] rotation of joint in model-frame
/// \return true if joint exists
bool getJointRotation(int jointIndex, glm::quat& rotation) const;
void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority);
/// Returns the extents of the model in its bind pose.
Extents getBindExtents() const;
/// Returns the extents of the model's mesh
Extents getMeshExtents() const;
void setScale(const glm::vec3& scale);
const glm::vec3& getScale() const { return _scale; }
/// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension
bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit
const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to
protected:
void setPupilDilation(float dilation) { _pupilDilation = dilation; }
float getPupilDilation() const { return _pupilDilation; }
void setBlendshapeCoefficients(const QVector<float>& coefficients) { _blendshapeCoefficients = coefficients; }
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
/// Returns the unscaled extents of the model's mesh
Extents getUnscaledMeshExtents() const;
@ -155,15 +209,6 @@ public:
/// Returns the scaled equivalent of a point in model space.
glm::vec3 calculateScaledOffsetPoint(const glm::vec3& point) const;
/// Returns a reference to the shared geometry.
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
/// Returns a reference to the shared collision geometry.
const QSharedPointer<NetworkGeometry> getCollisionGeometry(bool delayLoad = true);
/// Returns the number of joint states in the model.
int getJointStateCount() const { return _rig->getJointStateCount(); }
/// Fetches the joint state at the specified index.
/// \return whether or not the joint state is "valid" (that is, non-default)
bool getJointState(int index, glm::quat& rotation) const;
@ -171,25 +216,15 @@ public:
/// Fetches the visible joint state at the specified index.
/// \return whether or not the joint state is "valid" (that is, non-default)
bool getVisibleJointState(int index, glm::quat& rotation) const;
/// Clear the joint states
void clearJointState(int index);
/// Clear the joint animation priority
void clearJointAnimationPriority(int index);
/// Sets the joint state at the specified index.
void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f);
/// Returns the index of the parent of the indexed joint, or -1 if not found.
int getParentJointIndex(int jointIndex) const;
/// Returns the index of the last free ancestor of the indexed joint, or -1 if not found.
int getLastFreeJointIndex(int jointIndex) const;
bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const;
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;
bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const;
bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const;
bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;
@ -199,52 +234,15 @@ public:
/// \return true if joint exists
bool getJointPosition(int jointIndex, glm::vec3& position) const;
/// \param jointIndex index of joint in model structure
/// \param rotation[out] rotation of joint in model-frame
/// \return true if joint exists
bool getJointRotation(int jointIndex, glm::quat& rotation) const;
QStringList getJointNames() const;
AnimationHandlePointer createAnimationHandle();
const QList<AnimationHandlePointer>& getRunningAnimations() const { return _runningAnimations; }
// virtual overrides from PhysicsEntity
virtual void buildShapes();
virtual void updateShapePositions();
virtual void renderJointCollisionShapes(float alpha);
bool maybeStartBlender();
/// Sets blended vertices computed in a separate thread.
void setBlendedVertices(int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; }
// QVector<JointState>& getJointStates() { return _rig->getJointStates(); }
// const QVector<JointState>& getJointStates() const { return _jointStates; }
void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority);
Q_INVOKABLE void setTextureWithNameToURL(const QString& name, const QUrl& url)
{ _geometry->setTextureWithNameToURL(name, url); }
bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, QString& extraInfo, bool pickAgainstTriangles = false);
bool convexHullContains(glm::vec3 point);
AABox getPartBounds(int meshIndex, int partIndex);
void renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent);
bool initWhenReady(render::ScenePointer scene);
protected:
QSharedPointer<NetworkGeometry> _geometry;
void setGeometry(const QSharedPointer<NetworkGeometry>& newGeometry);
glm::vec3 _scale;
glm::vec3 _offset;
@ -257,21 +255,21 @@ protected:
bool _snapModelToRegistrationPoint; /// is the model's offset automatically adjusted to a registration point in model space
bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point
glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to
bool _showTrueJointTransforms;
class MeshState {
public:
QVector<glm::mat4> clusterMatrices;
};
QVector<MeshState> _meshStates;
// returns 'true' if needs fullUpdate after geometry change
bool updateGeometry();
virtual void initJointStates(QVector<JointState> states);
void setScaleInternal(const glm::vec3& scale);
void scaleToFit();
void snapToRegistrationPoint();
@ -298,11 +296,11 @@ protected:
/// the original position
/// \return true if the joint was found
bool restoreJointPosition(int jointIndex, float fraction = 1.0f, float priority = 0.0f);
/// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's
/// first free ancestor.
float getLimbLength(int jointIndex) const;
/// Allow sub classes to force invalidating the bboxes
void invalidCalculatedMeshBoxes() {
_calculatedMeshBoxesValid = false;
@ -316,28 +314,25 @@ protected:
// hook for derived classes to be notified when setUrl invalidates the current model.
virtual void onInvalidate() {};
protected slots:
void geometryRefreshed();
private:
friend class AnimationHandle;
void applyNextGeometry();
void deleteGeometry();
QVector<JointState> createJointStates(const FBXGeometry& geometry);
void initJointTransforms();
QSharedPointer<NetworkGeometry> _nextGeometry;
float _lodDistance;
float _lodHysteresis;
float _nextLODHysteresis;
QSharedPointer<NetworkGeometry> _collisionGeometry;
float _pupilDilation;
QVector<float> _blendshapeCoefficients;
QUrl _url;
QUrl _collisionUrl;
bool _isVisible;
@ -347,10 +342,6 @@ private:
gpu::Batch _renderBatch;
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
QSet<WeakAnimationHandlePointer> _animationHandles;
QList<AnimationHandlePointer> _runningAnimations;
QVector<float> _blendedBlendshapeCoefficients;
int _blendNumber;
@ -375,12 +366,12 @@ private:
QHash<QPair<int,int>, AABox> _calculatedMeshPartBoxes; // world coordinate AABoxes for all sub mesh part boxes
QHash<QPair<int,int>, qint64> _calculatedMeshPartOffset;
bool _calculatedMeshPartOffsetValid;
bool _calculatedMeshPartBoxesValid;
QVector<AABox> _calculatedMeshBoxes; // world coordinate AABoxes for all sub mesh boxes
bool _calculatedMeshBoxesValid;
QVector< QVector<Triangle> > _calculatedMeshTriangles; // world coordinate triangles for all sub meshes
bool _calculatedMeshTrianglesValid;
QMutex _mutex;
@ -420,10 +411,10 @@ private:
IS_SHADOW_FLAG,
IS_MIRROR_FLAG, //THis means that the mesh is rendered mirrored, not the same as "Rear view mirror"
IS_WIREFRAME_FLAG,
NUM_FLAGS,
};
enum Flag {
IS_TRANSLUCENT = (1 << IS_TRANSLUCENT_FLAG),
HAS_LIGHTMAP = (1 << HAS_LIGHTMAP_FLAG),
@ -489,7 +480,7 @@ private:
RenderKey(int bitmask) : _flags(bitmask) {}
};
class RenderPipeline {
public:
gpu::PipelinePointer _pipeline;
@ -503,7 +494,7 @@ private:
public:
typedef RenderKey Key;
void addRenderPipeline(Key key, gpu::ShaderPointer& vertexShader, gpu::ShaderPointer& pixelShader);
void initLocations(gpu::ShaderPointer& program, Locations& locations);
@ -511,8 +502,8 @@ private:
static RenderPipelineLib _renderPipelineLib;
bool _renderCollisionHull;
QSet<std::shared_ptr<MeshPartPayload>> _transparentRenderItems;
QSet<std::shared_ptr<MeshPartPayload>> _opaqueRenderItems;
QMap<render::ItemID, render::PayloadPointer> _renderItems;