From 4b22dadf0971c20749f9f0734f34762b704cb90c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 12 Nov 2014 11:53:22 -0800 Subject: [PATCH] first cut at implementing model scene rendering --- interface/src/avatar/MyAvatar.cpp | 1 + interface/src/entities/EntityTreeRenderer.cpp | 35 +- .../entities/RenderableModelEntityItem.cpp | 3 +- interface/src/renderer/AnimationHandle.cpp | 176 ++++++++ interface/src/renderer/AnimationHandle.h | 112 +++++ interface/src/renderer/Model.cpp | 391 +++++++++++------- interface/src/renderer/Model.h | 103 +---- 7 files changed, 578 insertions(+), 243 deletions(-) create mode 100644 interface/src/renderer/AnimationHandle.cpp create mode 100644 interface/src/renderer/AnimationHandle.h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9ceb073167..cab1913e14 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -39,6 +39,7 @@ #include "Recorder.h" #include "devices/Faceshift.h" #include "devices/OculusManager.h" +#include "renderer/AnimationHandle.h" #include "ui/TextRenderer.h" using namespace std; diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 1876b6c624..0107b43293 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -253,7 +253,40 @@ void EntityTreeRenderer::checkEnterLeaveEntities() { } void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode) { - OctreeRenderer::render(renderMode); + Model::startScene(); + //OctreeRenderer::render(renderMode); + + RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + if (_tree) { + _tree->lockForRead(); + _tree->recurseTreeWithOperation(renderOperation, &args); + _tree->unlock(); + } + + Model::RenderMode modelRenderMode = renderMode == RenderArgs::SHADOW_RENDER_MODE + ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; + + Model::endScene(modelRenderMode, &args); + + // stats... + _meshesConsidered = args._meshesConsidered; + _meshesRendered = args._meshesRendered; + _meshesOutOfView = args._meshesOutOfView; + _meshesTooSmall = args._meshesTooSmall; + + _elementsTouched = args._elementsTouched; + _itemsRendered = args._itemsRendered; + _itemsOutOfView = args._itemsOutOfView; + _itemsTooSmall = args._itemsTooSmall; + + _materialSwitches = args._materialSwitches; + _trianglesRendered = args._trianglesRendered; + _quadsRendered = args._quadsRendered; + + _translucentMeshPartsRendered = args._translucentMeshPartsRendered; + _opaqueMeshPartsRendered = args._opaqueMeshPartsRendered; + deleteReleasedModels(); // seems like as good as any other place to do some memory cleanup } diff --git a/interface/src/entities/RenderableModelEntityItem.cpp b/interface/src/entities/RenderableModelEntityItem.cpp index 3cee527273..7feecfafd7 100644 --- a/interface/src/entities/RenderableModelEntityItem.cpp +++ b/interface/src/entities/RenderableModelEntityItem.cpp @@ -172,7 +172,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) { // TODO: this is the majority of model render time. And rendering of a cube model vs the basic Box render // is significantly more expensive. Is there a way to call this that doesn't cost us as much? PerformanceTimer perfTimer("model->render"); - _model->render(alpha, modelRenderMode, args); + //_model->render(alpha, modelRenderMode, args); + _model->renderInScene(alpha, args); } else { // if we couldn't get a model, then just draw a cube glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]); diff --git a/interface/src/renderer/AnimationHandle.cpp b/interface/src/renderer/AnimationHandle.cpp new file mode 100644 index 0000000000..246ebaa9ed --- /dev/null +++ b/interface/src/renderer/AnimationHandle.cpp @@ -0,0 +1,176 @@ +// +// Model.cpp +// interface/src/renderer +// +// Created by Andrzej Kapolka on 10/18/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AnimationHandle.h" +#include "Application.h" + +void AnimationHandle::setURL(const QUrl& url) { + if (_url != url) { + _animation = Application::getInstance()->getAnimationCache()->getAnimation(_url = url); + _jointMappings.clear(); + } +} + +static void insertSorted(QList& handles, const AnimationHandlePointer& handle) { + for (QList::iterator it = handles.begin(); it != handles.end(); it++) { + if (handle->getPriority() > (*it)->getPriority()) { + handles.insert(it, handle); + return; + } + } + handles.append(handle); +} + +void AnimationHandle::setPriority(float priority) { + if (_priority == priority) { + return; + } + if (_running) { + _model->_runningAnimations.removeOne(_self); + if (priority < _priority) { + replaceMatchingPriorities(priority); + } + _priority = priority; + insertSorted(_model->_runningAnimations, _self); + + } else { + _priority = priority; + } +} + +void AnimationHandle::setStartAutomatically(bool startAutomatically) { + if ((_startAutomatically = startAutomatically) && !_running) { + start(); + } +} + +void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) { + _maskedJoints = maskedJoints; + _jointMappings.clear(); +} + +void AnimationHandle::setRunning(bool running) { + if (_running == running) { + if (running) { + // move back to the beginning + _frameIndex = _firstFrame; + } + return; + } + if ((_running = running)) { + if (!_model->_runningAnimations.contains(_self)) { + insertSorted(_model->_runningAnimations, _self); + } + _frameIndex = _firstFrame; + + } else { + _model->_runningAnimations.removeOne(_self); + replaceMatchingPriorities(0.0f); + } + emit runningChanged(_running); +} + +AnimationHandle::AnimationHandle(Model* model) : + QObject(model), + _model(model), + _fps(30.0f), + _priority(1.0f), + _loop(false), + _hold(false), + _startAutomatically(false), + _firstFrame(0.0f), + _lastFrame(FLT_MAX), + _running(false) { +} + +AnimationDetails AnimationHandle::getAnimationDetails() const { + AnimationDetails details(_role, _url, _fps, _priority, _loop, _hold, + _startAutomatically, _firstFrame, _lastFrame, _running, _frameIndex); + return details; +} + + +void AnimationHandle::simulate(float deltaTime) { + _frameIndex += deltaTime * _fps; + + // update the joint mappings if necessary/possible + if (_jointMappings.isEmpty()) { + if (_model->isActive()) { + _jointMappings = _model->getGeometry()->getJointMappings(_animation); + } + if (_jointMappings.isEmpty()) { + return; + } + if (!_maskedJoints.isEmpty()) { + const FBXGeometry& geometry = _model->getGeometry()->getFBXGeometry(); + for (int i = 0; i < _jointMappings.size(); i++) { + int& mapping = _jointMappings[i]; + if (mapping != -1 && _maskedJoints.contains(geometry.joints.at(mapping).name)) { + mapping = -1; + } + } + } + } + + const FBXGeometry& animationGeometry = _animation->getGeometry(); + if (animationGeometry.animationFrames.isEmpty()) { + stop(); + return; + } + float endFrameIndex = qMin(_lastFrame, animationGeometry.animationFrames.size() - (_loop ? 0.0f : 1.0f)); + float startFrameIndex = qMin(_firstFrame, endFrameIndex); + if ((!_loop && (_frameIndex < startFrameIndex || _frameIndex > endFrameIndex)) || startFrameIndex == endFrameIndex) { + // passed the end; apply the last frame + applyFrame(glm::clamp(_frameIndex, startFrameIndex, endFrameIndex)); + if (!_hold) { + stop(); + } + return; + } + // wrap within the the desired range + if (_frameIndex < startFrameIndex) { + _frameIndex = endFrameIndex - glm::mod(endFrameIndex - _frameIndex, endFrameIndex - startFrameIndex); + + } else if (_frameIndex > endFrameIndex) { + _frameIndex = startFrameIndex + glm::mod(_frameIndex - startFrameIndex, endFrameIndex - startFrameIndex); + } + + // blend between the closest two frames + applyFrame(_frameIndex); +} + +void AnimationHandle::applyFrame(float frameIndex) { + const FBXGeometry& animationGeometry = _animation->getGeometry(); + int frameCount = animationGeometry.animationFrames.size(); + const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount); + const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount); + float frameFraction = glm::fract(frameIndex); + for (int i = 0; i < _jointMappings.size(); i++) { + int mapping = _jointMappings.at(i); + if (mapping != -1) { + JointState& state = _model->_jointStates[mapping]; + state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction), _priority); + } + } +} + +void AnimationHandle::replaceMatchingPriorities(float newPriority) { + for (int i = 0; i < _jointMappings.size(); i++) { + int mapping = _jointMappings.at(i); + if (mapping != -1) { + JointState& state = _model->_jointStates[mapping]; + if (_priority == state._animationPriority) { + state._animationPriority = newPriority; + } + } + } +} + diff --git a/interface/src/renderer/AnimationHandle.h b/interface/src/renderer/AnimationHandle.h new file mode 100644 index 0000000000..3b736698df --- /dev/null +++ b/interface/src/renderer/AnimationHandle.h @@ -0,0 +1,112 @@ +// +// AnimationHandle.h +// interface/src/renderer +// +// Created by Andrzej Kapolka on 10/18/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AnimationHandle_h +#define hifi_AnimationHandle_h + +#include +#include +#include +#include +#include + +#include + +class AnimationHandle; +class Model; + +typedef QSharedPointer AnimationHandlePointer; +typedef QWeakPointer WeakAnimationHandlePointer; + + +/// Represents a handle to a model animation. +class AnimationHandle : public QObject { + Q_OBJECT + +public: + + void setRole(const QString& role) { _role = role; } + const QString& getRole() const { return _role; } + + void setURL(const QUrl& url); + const QUrl& getURL() const { return _url; } + + void setFPS(float fps) { _fps = fps; } + float getFPS() const { return _fps; } + + void setPriority(float priority); + float getPriority() const { return _priority; } + + void setLoop(bool loop) { _loop = loop; } + bool getLoop() const { return _loop; } + + void setHold(bool hold) { _hold = hold; } + bool getHold() const { return _hold; } + + void setStartAutomatically(bool startAutomatically); + bool getStartAutomatically() const { return _startAutomatically; } + + void setFirstFrame(float firstFrame) { _firstFrame = firstFrame; } + float getFirstFrame() const { return _firstFrame; } + + void setLastFrame(float lastFrame) { _lastFrame = lastFrame; } + float getLastFrame() const { return _lastFrame; } + + void setMaskedJoints(const QStringList& maskedJoints); + const QStringList& getMaskedJoints() const { return _maskedJoints; } + + void setRunning(bool running); + bool isRunning() const { return _running; } + + void setFrameIndex(float frameIndex) { _frameIndex = glm::clamp(_frameIndex, _firstFrame, _lastFrame); } + float getFrameIndex() const { return _frameIndex; } + + AnimationDetails getAnimationDetails() const; + +signals: + + void runningChanged(bool running); + +public slots: + + void start() { setRunning(true); } + void stop() { setRunning(false); } + +private: + + friend class Model; + + AnimationHandle(Model* model); + + void simulate(float deltaTime); + void applyFrame(float frameIndex); + void replaceMatchingPriorities(float newPriority); + + Model* _model; + WeakAnimationHandlePointer _self; + AnimationPointer _animation; + QString _role; + QUrl _url; + float _fps; + float _priority; + bool _loop; + bool _hold; + bool _startAutomatically; + float _firstFrame; + float _lastFrame; + QStringList _maskedJoints; + bool _running; + QVector _jointMappings; + float _frameIndex; +}; + + +#endif // hifi_AnimationHandle_h diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 8d426d067b..6252e7c2d7 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -23,6 +23,7 @@ #include #include +#include "AnimationHandle.h" #include "Application.h" #include "Model.h" @@ -517,17 +518,7 @@ void Model::recalcuateMeshBoxes() { } } -bool Model::render(float alpha, RenderMode mode, RenderArgs* args) { - PROFILE_RANGE(__FUNCTION__); - - // render the attachments - foreach (Model* attachment, _attachments) { - attachment->render(alpha, mode); - } - if (_meshStates.isEmpty()) { - return false; - } - +void Model::renderSetup(RenderArgs* args) { // if we don't have valid mesh boxes, calculate them now, this only matters in cases // where our caller has passed RenderArgs which will include a view frustum we can cull // against. We cache the results of these calculations so long as the model hasn't been @@ -549,6 +540,25 @@ bool Model::render(float alpha, RenderMode mode, RenderArgs* args) { if (!_meshGroupsKnown) { segregateMeshGroups(); } +} + +bool Model::render(float alpha, RenderMode mode, RenderArgs* args) { + PROFILE_RANGE(__FUNCTION__); + + // render the attachments + foreach (Model* attachment, _attachments) { + attachment->render(alpha, mode); + } + if (_meshStates.isEmpty()) { + return false; + } + + renderSetup(args); + return renderCore(alpha, mode, args); +} + +bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { + PROFILE_RANGE(__FUNCTION__); // Let's introduce a gpu::Batch to capture all the calls to the graphics api gpu::Batch batch; @@ -1981,165 +1991,236 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl return meshPartsRendered; } -void AnimationHandle::setURL(const QUrl& url) { - if (_url != url) { - _animation = Application::getInstance()->getAnimationCache()->getAnimation(_url = url); - _jointMappings.clear(); - } + + +// Scene rendering support +QVector Model::_modelsInScene; +void Model::startScene() { + _modelsInScene.clear(); } -static void insertSorted(QList& handles, const AnimationHandlePointer& handle) { - for (QList::iterator it = handles.begin(); it != handles.end(); it++) { - if (handle->getPriority() > (*it)->getPriority()) { - handles.insert(it, handle); - return; - } - } - handles.append(handle); -} +void Model::endSceneSplitPass(RenderMode mode, RenderArgs* args) { -void AnimationHandle::setPriority(float priority) { - if (_priority == priority) { - return; - } - if (_running) { - _model->_runningAnimations.removeOne(_self); - if (priority < _priority) { - replaceMatchingPriorities(priority); - } - _priority = priority; - insertSorted(_model->_runningAnimations, _self); - + // first, do all the batch/GPU setup work.... + // Let's introduce a gpu::Batch to capture all the calls to the graphics api + gpu::Batch batch; + + + GLBATCH(glDisable)(GL_COLOR_MATERIAL); + + if (mode == DIFFUSE_RENDER_MODE || mode == NORMAL_RENDER_MODE) { + GLBATCH(glDisable)(GL_CULL_FACE); } else { - _priority = priority; - } -} - -void AnimationHandle::setStartAutomatically(bool startAutomatically) { - if ((_startAutomatically = startAutomatically) && !_running) { - start(); - } -} - -void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) { - _maskedJoints = maskedJoints; - _jointMappings.clear(); -} - -void AnimationHandle::setRunning(bool running) { - if (_running == running) { - if (running) { - // move back to the beginning - _frameIndex = _firstFrame; - } - return; - } - if ((_running = running)) { - if (!_model->_runningAnimations.contains(_self)) { - insertSorted(_model->_runningAnimations, _self); - } - _frameIndex = _firstFrame; - - } else { - _model->_runningAnimations.removeOne(_self); - replaceMatchingPriorities(0.0f); - } - emit runningChanged(_running); -} - -AnimationHandle::AnimationHandle(Model* model) : - QObject(model), - _model(model), - _fps(30.0f), - _priority(1.0f), - _loop(false), - _hold(false), - _startAutomatically(false), - _firstFrame(0.0f), - _lastFrame(FLT_MAX), - _running(false) { -} - -AnimationDetails AnimationHandle::getAnimationDetails() const { - AnimationDetails details(_role, _url, _fps, _priority, _loop, _hold, - _startAutomatically, _firstFrame, _lastFrame, _running, _frameIndex); - return details; -} - - -void AnimationHandle::simulate(float deltaTime) { - _frameIndex += deltaTime * _fps; - - // update the joint mappings if necessary/possible - if (_jointMappings.isEmpty()) { - if (_model->isActive()) { - _jointMappings = _model->getGeometry()->getJointMappings(_animation); - } - if (_jointMappings.isEmpty()) { - return; - } - if (!_maskedJoints.isEmpty()) { - const FBXGeometry& geometry = _model->getGeometry()->getFBXGeometry(); - for (int i = 0; i < _jointMappings.size(); i++) { - int& mapping = _jointMappings[i]; - if (mapping != -1 && _maskedJoints.contains(geometry.joints.at(mapping).name)) { - mapping = -1; - } - } + GLBATCH(glEnable)(GL_CULL_FACE); + if (mode == SHADOW_RENDER_MODE) { + GLBATCH(glCullFace)(GL_FRONT); } } - const FBXGeometry& animationGeometry = _animation->getGeometry(); - if (animationGeometry.animationFrames.isEmpty()) { - stop(); - return; - } - float endFrameIndex = qMin(_lastFrame, animationGeometry.animationFrames.size() - (_loop ? 0.0f : 1.0f)); - float startFrameIndex = qMin(_firstFrame, endFrameIndex); - if ((!_loop && (_frameIndex < startFrameIndex || _frameIndex > endFrameIndex)) || startFrameIndex == endFrameIndex) { - // passed the end; apply the last frame - applyFrame(glm::clamp(_frameIndex, startFrameIndex, endFrameIndex)); - if (!_hold) { - stop(); - } - return; - } - // wrap within the the desired range - if (_frameIndex < startFrameIndex) { - _frameIndex = endFrameIndex - glm::mod(endFrameIndex - _frameIndex, endFrameIndex - startFrameIndex); + // render opaque meshes with alpha testing + + GLBATCH(glDisable)(GL_BLEND); + GLBATCH(glEnable)(GL_ALPHA_TEST); - } else if (_frameIndex > endFrameIndex) { - _frameIndex = startFrameIndex + glm::mod(_frameIndex - startFrameIndex, endFrameIndex - startFrameIndex); + if (mode == SHADOW_RENDER_MODE) { + GLBATCH(glAlphaFunc)(GL_EQUAL, 0.0f); + } + + + /*Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers( + mode == DEFAULT_RENDER_MODE || mode == DIFFUSE_RENDER_MODE, + mode == DEFAULT_RENDER_MODE || mode == NORMAL_RENDER_MODE, + mode == DEFAULT_RENDER_MODE); + */ + { + GLenum buffers[3]; + int bufferCount = 0; + if (mode == DEFAULT_RENDER_MODE || mode == DIFFUSE_RENDER_MODE) { + buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; + } + if (mode == DEFAULT_RENDER_MODE || mode == NORMAL_RENDER_MODE) { + buffers[bufferCount++] = GL_COLOR_ATTACHMENT1; + } + if (mode == DEFAULT_RENDER_MODE) { + buffers[bufferCount++] = GL_COLOR_ATTACHMENT2; + } + GLBATCH(glDrawBuffers)(bufferCount, buffers); + } + + const float DEFAULT_ALPHA_THRESHOLD = 0.5f; + + int opaqueMeshPartsRendered = 0; + + // now, for each model in the scene, render the mesh portions + float alpha = 1.0f; // at this point we don't support per model alphas + foreach(Model* model, _modelsInScene) { + opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, args); + } + foreach(Model* model, _modelsInScene) { + opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, args); + } + foreach(Model* model, _modelsInScene) { + opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, args); + } + foreach(Model* model, _modelsInScene) { + opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, args); + } + foreach(Model* model, _modelsInScene) { + opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, args); + } + foreach(Model* model, _modelsInScene) { + opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args); + } + foreach(Model* model, _modelsInScene) { + opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, args); + } + foreach(Model* model, _modelsInScene) { + opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args); } - // blend between the closest two frames - applyFrame(_frameIndex); -} + // render translucent meshes afterwards + //Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(false, true, true); + { + GLenum buffers[2]; + int bufferCount = 0; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT1; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT2; + GLBATCH(glDrawBuffers)(bufferCount, buffers); + } -void AnimationHandle::applyFrame(float frameIndex) { - const FBXGeometry& animationGeometry = _animation->getGeometry(); - int frameCount = animationGeometry.animationFrames.size(); - const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount); - const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount); - float frameFraction = glm::fract(frameIndex); - for (int i = 0; i < _jointMappings.size(); i++) { - int mapping = _jointMappings.at(i); - if (mapping != -1) { - JointState& state = _model->_jointStates[mapping]; - state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction), _priority); + int translucentMeshPartsRendered = 0; + const float MOSTLY_OPAQUE_THRESHOLD = 0.75f; + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, args); + } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, args); + } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, args); + } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, args); + } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, false, args); + } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args); + } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, true, false, args); + } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args); + } + + GLBATCH(glDisable)(GL_ALPHA_TEST); + GLBATCH(glEnable)(GL_BLEND); + GLBATCH(glDepthMask)(false); + GLBATCH(glDepthFunc)(GL_LEQUAL); + + //Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true); + { + GLenum buffers[1]; + int bufferCount = 0; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; + GLBATCH(glDrawBuffers)(bufferCount, buffers); + } + + if (mode == DEFAULT_RENDER_MODE || mode == DIFFUSE_RENDER_MODE) { + const float MOSTLY_TRANSPARENT_THRESHOLD = 0.0f; + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, args); } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, args); + } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, args); + } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, args); + } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, false, args); + } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args); + } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, true, false, args); + } + foreach(Model* model, _modelsInScene) { + translucentMeshPartsRendered += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args); + } + } + + GLBATCH(glDepthMask)(true); + GLBATCH(glDepthFunc)(GL_LESS); + GLBATCH(glDisable)(GL_CULL_FACE); + + if (mode == SHADOW_RENDER_MODE) { + GLBATCH(glCullFace)(GL_BACK); + } + + // deactivate vertex arrays after drawing + GLBATCH(glDisableClientState)(GL_NORMAL_ARRAY); + GLBATCH(glDisableClientState)(GL_VERTEX_ARRAY); + GLBATCH(glDisableClientState)(GL_TEXTURE_COORD_ARRAY); + GLBATCH(glDisableClientState)(GL_COLOR_ARRAY); + GLBATCH(glDisableVertexAttribArray)(gpu::Stream::TANGENT); + GLBATCH(glDisableVertexAttribArray)(gpu::Stream::SKIN_CLUSTER_INDEX); + GLBATCH(glDisableVertexAttribArray)(gpu::Stream::SKIN_CLUSTER_WEIGHT); + + // bind with 0 to switch back to normal operation + GLBATCH(glBindBuffer)(GL_ARRAY_BUFFER, 0); + GLBATCH(glBindBuffer)(GL_ELEMENT_ARRAY_BUFFER, 0); + GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); + + // Render! + { + PROFILE_RANGE("render Batch"); + ::gpu::GLBackend::renderBatch(batch); + batch.clear(); + } + + // restore all the default material settings + Application::getInstance()->setupWorldLight(); + + if (args) { + args->_translucentMeshPartsRendered = translucentMeshPartsRendered; + args->_opaqueMeshPartsRendered = opaqueMeshPartsRendered; } } -void AnimationHandle::replaceMatchingPriorities(float newPriority) { - for (int i = 0; i < _jointMappings.size(); i++) { - int mapping = _jointMappings.at(i); - if (mapping != -1) { - JointState& state = _model->_jointStates[mapping]; - if (_priority == state._animationPriority) { - state._animationPriority = newPriority; - } - } +void Model::endScene(RenderMode mode, RenderArgs* args) { + //endSceneSimple(mode, args); + endSceneSplitPass(mode, args); +} + +void Model::endSceneSimple(RenderMode mode, RenderArgs* args) { + // now, for each model in the scene, render the mesh portions + foreach(Model* model, _modelsInScene) { + float alpha = 1.0f; + model->render(alpha, mode, args); } } +bool Model::renderInScene(float alpha, RenderArgs* args) { + // do we really need alpha? + + // render the attachments + foreach (Model* attachment, _attachments) { + attachment->renderInScene(alpha); + } + if (_meshStates.isEmpty()) { + return false; + } + + renderSetup(args); + + _modelsInScene.push_back(this); + return true; +} + diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 34d6a34c06..d77501e0f9 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -20,6 +20,7 @@ #include #include +#include "AnimationHandle.h" #include "GeometryCache.h" #include "InterfaceConfig.h" #include "JointState.h" @@ -28,14 +29,10 @@ class QScriptEngine; -class AnimationHandle; class Shape; class RenderArgs; class ViewFrustum; -typedef QSharedPointer AnimationHandlePointer; -typedef QWeakPointer WeakAnimationHandlePointer; - namespace gpu { class Batch; } @@ -93,6 +90,12 @@ public: enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE }; bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE, RenderArgs* args = NULL); + bool renderCore(float alpha, RenderMode mode, RenderArgs* args); + + // Scene rendering support + static void startScene(); + bool renderInScene(float alpha = 1.0f, RenderArgs* args = NULL); + static void endScene(RenderMode mode = DEFAULT_RENDER_MODE, RenderArgs* args = NULL); /// 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 @@ -259,14 +262,13 @@ protected: /// 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; - + private: friend class AnimationHandle; void applyNextGeometry(); void deleteGeometry(); - int renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args = NULL); QVector createJointStates(const FBXGeometry& geometry); void initJointTransforms(); @@ -394,91 +396,20 @@ private: QVector _meshesOpaqueTangentsSpecularSkinned; QVector _meshesOpaqueSpecularSkinned; + // Scene rendering support + static QVector _modelsInScene; + static void endSceneSimple(RenderMode mode = DEFAULT_RENDER_MODE, RenderArgs* args = NULL); + static void endSceneSplitPass(RenderMode mode = DEFAULT_RENDER_MODE, RenderArgs* args = NULL); + + // helper functions used by render() or renderInScene() + void renderSetup(RenderArgs* args); + int renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, + bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args = NULL); }; Q_DECLARE_METATYPE(QPointer) Q_DECLARE_METATYPE(QWeakPointer) Q_DECLARE_METATYPE(QVector) -/// Represents a handle to a model animation. -class AnimationHandle : public QObject { - Q_OBJECT - -public: - - void setRole(const QString& role) { _role = role; } - const QString& getRole() const { return _role; } - - void setURL(const QUrl& url); - const QUrl& getURL() const { return _url; } - - void setFPS(float fps) { _fps = fps; } - float getFPS() const { return _fps; } - - void setPriority(float priority); - float getPriority() const { return _priority; } - - void setLoop(bool loop) { _loop = loop; } - bool getLoop() const { return _loop; } - - void setHold(bool hold) { _hold = hold; } - bool getHold() const { return _hold; } - - void setStartAutomatically(bool startAutomatically); - bool getStartAutomatically() const { return _startAutomatically; } - - void setFirstFrame(float firstFrame) { _firstFrame = firstFrame; } - float getFirstFrame() const { return _firstFrame; } - - void setLastFrame(float lastFrame) { _lastFrame = lastFrame; } - float getLastFrame() const { return _lastFrame; } - - void setMaskedJoints(const QStringList& maskedJoints); - const QStringList& getMaskedJoints() const { return _maskedJoints; } - - void setRunning(bool running); - bool isRunning() const { return _running; } - - void setFrameIndex(float frameIndex) { _frameIndex = glm::clamp(_frameIndex, _firstFrame, _lastFrame); } - float getFrameIndex() const { return _frameIndex; } - - AnimationDetails getAnimationDetails() const; - -signals: - - void runningChanged(bool running); - -public slots: - - void start() { setRunning(true); } - void stop() { setRunning(false); } - -private: - - friend class Model; - - AnimationHandle(Model* model); - - void simulate(float deltaTime); - void applyFrame(float frameIndex); - void replaceMatchingPriorities(float newPriority); - - Model* _model; - WeakAnimationHandlePointer _self; - AnimationPointer _animation; - QString _role; - QUrl _url; - float _fps; - float _priority; - bool _loop; - bool _hold; - bool _startAutomatically; - float _firstFrame; - float _lastFrame; - QStringList _maskedJoints; - bool _running; - QVector _jointMappings; - float _frameIndex; -}; #endif // hifi_Model_h