Merge pull request #3783 from ZappoMan/modelSceneRendering

Model scene rendering
This commit is contained in:
Philip Rosedale 2014-11-12 20:38:02 -08:00
commit 2c4335d1c5
10 changed files with 659 additions and 266 deletions

View file

@ -160,6 +160,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_lastQueriedViewFrustum(),
_lastQueriedTime(usecTimestampNow()),
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
_viewTransform(new gpu::Transform()),
_scaleMirror(1.0f),
_rotateMirror(0.0f),
_raiseMirror(0.0f),
@ -188,8 +189,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_lastNackTime(usecTimestampNow()),
_lastSendDownstreamAudioStats(usecTimestampNow()),
_isVSyncOn(true),
_aboutToQuit(false),
_viewTransform(new gpu::Transform())
_aboutToQuit(false)
{
// read the ApplicationInfo.ini file for Name/Version/Domain information

View file

@ -437,10 +437,15 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DisplayModelElementChildProxies, 0, false);
addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DisableLightEntities, 0, false);
addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DontReduceMaterialSwitches, 0, false);
addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::RenderEntitiesAsScene, 0, false);
QMenu* entityCullingMenu = entitiesDebugMenu->addMenu("Culling");
addCheckableActionToQMenuAndActionHash(entityCullingMenu, MenuOption::DontCullOutOfViewMeshParts, 0, false);
addCheckableActionToQMenuAndActionHash(entityCullingMenu, MenuOption::DontCullTooSmallMeshParts, 0, false);
addCheckableActionToQMenuAndActionHash(entityCullingMenu, MenuOption::DontReduceMaterialSwitches, 0, false);
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxels");
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures);

View file

@ -447,6 +447,7 @@ namespace MenuOption {
const QString ReloadAllScripts = "Reload All Scripts";
const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes";
const QString RenderDualContourSurfaces = "Render Dual Contour Surfaces";
const QString RenderEntitiesAsScene = "Render Entities as Scene";
const QString RenderFocusIndicator = "Show Eye Focus";
const QString RenderHeadCollisionShapes = "Show Head Collision Shapes";
const QString RenderHeightfields = "Render Heightfields";

View file

@ -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;

View file

@ -253,7 +253,45 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
}
void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode) {
OctreeRenderer::render(renderMode);
bool dontRenderAsScene = !Menu::getInstance()->isOptionChecked(MenuOption::RenderEntitiesAsScene);
if (dontRenderAsScene) {
OctreeRenderer::render(renderMode);
} else {
if (_tree) {
Model::startScene();
RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
_tree->lockForRead();
_tree->recurseTreeWithOperation(renderOperation, &args);
Model::RenderMode modelRenderMode = renderMode == RenderArgs::SHADOW_RENDER_MODE
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
// we must call endScene while we still have the tree locked so that no one deletes a model
// on us while rendering the scene
Model::endScene(modelRenderMode, &args);
_tree->unlock();
// 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
}

View file

@ -172,7 +172,12 @@ 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);
bool dontRenderAsScene = !Menu::getInstance()->isOptionChecked(MenuOption::RenderEntitiesAsScene);
if (dontRenderAsScene) {
_model->render(alpha, modelRenderMode, args);
} else {
_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]);

View file

@ -0,0 +1,176 @@
//
// AnimationHandle.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<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
for (QList<AnimationHandlePointer>::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;
}
}
}
}

View file

@ -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 <QObject>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QVector>
#include <AnimationCache.h>
class AnimationHandle;
class Model;
typedef QSharedPointer<AnimationHandle> AnimationHandlePointer;
typedef QWeakPointer<AnimationHandle> 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<int> _jointMappings;
float _frameIndex;
};
#endif // hifi_AnimationHandle_h

View file

@ -23,6 +23,7 @@
#include <ShapeCollider.h>
#include <SphereShape.h>
#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
_renderBatch.clear();
@ -1471,6 +1481,279 @@ void Model::deleteGeometry() {
_blendedBlendshapeCoefficients.clear();
}
// Scene rendering support
QVector<Model*> Model::_modelsInScene;
gpu::Batch Model::_sceneRenderBatch;
void Model::startScene() {
_modelsInScene.clear();
}
void Model::setupBatchTransform(gpu::Batch& batch) {
GLBATCH(glPushMatrix)();
// Capture the view matrix once for the rendering of this model
if (_transforms.empty()) {
_transforms.push_back(gpu::TransformPointer(new gpu::Transform()));
}
(*_transforms[0]) = gpu::Transform((*Application::getInstance()->getViewTransform()));
_transforms[0]->preTranslate(-_translation);
batch.setViewTransform(_transforms[0]);
}
void Model::endScene(RenderMode mode, RenderArgs* args) {
PROFILE_RANGE(__FUNCTION__);
// Let's introduce a gpu::Batch to capture all the calls to the graphics api
_sceneRenderBatch.clear();
gpu::Batch& batch = _sceneRenderBatch;
GLBATCH(glDisable)(GL_COLOR_MATERIAL);
if (mode == DIFFUSE_RENDER_MODE || mode == NORMAL_RENDER_MODE) {
GLBATCH(glDisable)(GL_CULL_FACE);
} else {
GLBATCH(glEnable)(GL_CULL_FACE);
if (mode == SHADOW_RENDER_MODE) {
GLBATCH(glCullFace)(GL_FRONT);
}
}
// render opaque meshes with alpha testing
GLBATCH(glDisable)(GL_BLEND);
GLBATCH(glEnable)(GL_ALPHA_TEST);
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
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args);
GLBATCH(glPopMatrix)();
}
// 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);
}
int translucentParts = 0;
const float MOSTLY_OPAQUE_THRESHOLD = 0.75f;
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, false, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, true, false, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args);
GLBATCH(glPopMatrix)();
}
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) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, false, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, true, false, args);
GLBATCH(glPopMatrix)();
}
foreach(Model* model, _modelsInScene) {
model->setupBatchTransform(batch);
translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args);
GLBATCH(glPopMatrix)();
}
}
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);
}
// restore all the default material settings
Application::getInstance()->setupWorldLight();
if (args) {
args->_translucentMeshPartsRendered = translucentParts;
args->_opaqueMeshPartsRendered = opaqueMeshPartsRendered;
}
}
bool Model::renderInScene(float alpha, RenderArgs* args) {
// render the attachments
foreach (Model* attachment, _attachments) {
attachment->renderInScene(alpha);
}
if (_meshStates.isEmpty()) {
return false;
}
renderSetup(args);
_modelsInScene.push_back(this);
return true;
}
void Model::segregateMeshGroups() {
_meshesTranslucentTangents.clear();
_meshesTranslucent.clear();
@ -1989,166 +2272,3 @@ 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();
}
}
static void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
for (QList<AnimationHandlePointer>::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;
}
}
}
}

View file

@ -21,6 +21,7 @@
#include <AnimationCache.h>
#include <PhysicsEntity.h>
#include "AnimationHandle.h"
#include "GeometryCache.h"
#include "InterfaceConfig.h"
#include "JointState.h"
@ -29,14 +30,10 @@
class QScriptEngine;
class AnimationHandle;
class Shape;
class RenderArgs;
class ViewFrustum;
typedef QSharedPointer<AnimationHandle> AnimationHandlePointer;
typedef QWeakPointer<AnimationHandle> WeakAnimationHandlePointer;
#include "gpu/Stream.h"
#include "gpu/Batch.h"
@ -92,6 +89,11 @@ 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);
// 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
@ -258,14 +260,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<JointState> createJointStates(const FBXGeometry& geometry);
void initJointTransforms();
@ -283,7 +284,7 @@ private:
gpu::Buffers _blendedVertexBuffers;
gpu::Transforms _transforms;
gpu::Batch _renderBatch;
gpu::Batch _renderBatch;
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
@ -395,91 +396,25 @@ private:
QVector<int> _meshesOpaqueTangentsSpecularSkinned;
QVector<int> _meshesOpaqueSpecularSkinned;
// Scene rendering support
static QVector<Model*> _modelsInScene;
static gpu::Batch _sceneRenderBatch;
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);
bool renderCore(float alpha, RenderMode mode, RenderArgs* args);
int renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,
bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args = NULL);
void setupBatchTransform(gpu::Batch& batch);
};
Q_DECLARE_METATYPE(QPointer<Model>)
Q_DECLARE_METATYPE(QWeakPointer<NetworkGeometry>)
Q_DECLARE_METATYPE(QVector<glm::vec3>)
/// 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<int> _jointMappings;
float _frameIndex;
};
#endif // hifi_Model_h