mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-17 03:30:30 +02:00
Merge pull request #3783 from ZappoMan/modelSceneRendering
Model scene rendering
This commit is contained in:
commit
2c4335d1c5
10 changed files with 659 additions and 266 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
176
interface/src/renderer/AnimationHandle.cpp
Normal file
176
interface/src/renderer/AnimationHandle.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
112
interface/src/renderer/AnimationHandle.h
Normal file
112
interface/src/renderer/AnimationHandle.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue