mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
first cut at implementing model scene rendering
This commit is contained in:
parent
e6bbb53483
commit
4b22dadf09
7 changed files with 578 additions and 243 deletions
|
@ -39,6 +39,7 @@
|
||||||
#include "Recorder.h"
|
#include "Recorder.h"
|
||||||
#include "devices/Faceshift.h"
|
#include "devices/Faceshift.h"
|
||||||
#include "devices/OculusManager.h"
|
#include "devices/OculusManager.h"
|
||||||
|
#include "renderer/AnimationHandle.h"
|
||||||
#include "ui/TextRenderer.h"
|
#include "ui/TextRenderer.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
|
@ -253,7 +253,40 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode) {
|
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
|
deleteReleasedModels(); // seems like as good as any other place to do some memory cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
// 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?
|
// is significantly more expensive. Is there a way to call this that doesn't cost us as much?
|
||||||
PerformanceTimer perfTimer("model->render");
|
PerformanceTimer perfTimer("model->render");
|
||||||
_model->render(alpha, modelRenderMode, args);
|
//_model->render(alpha, modelRenderMode, args);
|
||||||
|
_model->renderInScene(alpha, args);
|
||||||
} else {
|
} else {
|
||||||
// if we couldn't get a model, then just draw a cube
|
// if we couldn't get a model, then just draw a cube
|
||||||
glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]);
|
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 @@
|
||||||
|
//
|
||||||
|
// 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<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 <ShapeCollider.h>
|
||||||
#include <SphereShape.h>
|
#include <SphereShape.h>
|
||||||
|
|
||||||
|
#include "AnimationHandle.h"
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Model.h"
|
#include "Model.h"
|
||||||
|
|
||||||
|
@ -517,17 +518,7 @@ void Model::recalcuateMeshBoxes() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::render(float alpha, RenderMode mode, RenderArgs* args) {
|
void Model::renderSetup(RenderArgs* args) {
|
||||||
PROFILE_RANGE(__FUNCTION__);
|
|
||||||
|
|
||||||
// render the attachments
|
|
||||||
foreach (Model* attachment, _attachments) {
|
|
||||||
attachment->render(alpha, mode);
|
|
||||||
}
|
|
||||||
if (_meshStates.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we don't have valid mesh boxes, calculate them now, this only matters in cases
|
// 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
|
// 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
|
// 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) {
|
if (!_meshGroupsKnown) {
|
||||||
segregateMeshGroups();
|
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
|
// Let's introduce a gpu::Batch to capture all the calls to the graphics api
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
@ -1981,165 +1991,236 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl
|
||||||
return meshPartsRendered;
|
return meshPartsRendered;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationHandle::setURL(const QUrl& url) {
|
|
||||||
if (_url != url) {
|
|
||||||
_animation = Application::getInstance()->getAnimationCache()->getAnimation(_url = url);
|
// Scene rendering support
|
||||||
_jointMappings.clear();
|
QVector<Model*> Model::_modelsInScene;
|
||||||
}
|
void Model::startScene() {
|
||||||
|
_modelsInScene.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
|
void Model::endSceneSplitPass(RenderMode mode, RenderArgs* args) {
|
||||||
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) {
|
// first, do all the batch/GPU setup work....
|
||||||
if (_priority == priority) {
|
// Let's introduce a gpu::Batch to capture all the calls to the graphics api
|
||||||
return;
|
gpu::Batch batch;
|
||||||
}
|
|
||||||
if (_running) {
|
|
||||||
_model->_runningAnimations.removeOne(_self);
|
|
||||||
if (priority < _priority) {
|
|
||||||
replaceMatchingPriorities(priority);
|
|
||||||
}
|
|
||||||
_priority = priority;
|
|
||||||
insertSorted(_model->_runningAnimations, _self);
|
|
||||||
|
|
||||||
|
|
||||||
|
GLBATCH(glDisable)(GL_COLOR_MATERIAL);
|
||||||
|
|
||||||
|
if (mode == DIFFUSE_RENDER_MODE || mode == NORMAL_RENDER_MODE) {
|
||||||
|
GLBATCH(glDisable)(GL_CULL_FACE);
|
||||||
} else {
|
} else {
|
||||||
_priority = priority;
|
GLBATCH(glEnable)(GL_CULL_FACE);
|
||||||
|
if (mode == SHADOW_RENDER_MODE) {
|
||||||
|
GLBATCH(glCullFace)(GL_FRONT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationHandle::setStartAutomatically(bool startAutomatically) {
|
// render opaque meshes with alpha testing
|
||||||
if ((_startAutomatically = startAutomatically) && !_running) {
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) {
|
GLBATCH(glDisable)(GL_BLEND);
|
||||||
_maskedJoints = maskedJoints;
|
GLBATCH(glEnable)(GL_ALPHA_TEST);
|
||||||
_jointMappings.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimationHandle::setRunning(bool running) {
|
if (mode == SHADOW_RENDER_MODE) {
|
||||||
if (_running == running) {
|
GLBATCH(glAlphaFunc)(GL_EQUAL, 0.0f);
|
||||||
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) {
|
/*Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(
|
||||||
_frameIndex += deltaTime * _fps;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
// update the joint mappings if necessary/possible
|
const float DEFAULT_ALPHA_THRESHOLD = 0.5f;
|
||||||
if (_jointMappings.isEmpty()) {
|
|
||||||
if (_model->isActive()) {
|
int opaqueMeshPartsRendered = 0;
|
||||||
_jointMappings = _model->getGeometry()->getJointMappings(_animation);
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
if (_jointMappings.isEmpty()) {
|
foreach(Model* model, _modelsInScene) {
|
||||||
return;
|
opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, args);
|
||||||
}
|
}
|
||||||
if (!_maskedJoints.isEmpty()) {
|
foreach(Model* model, _modelsInScene) {
|
||||||
const FBXGeometry& geometry = _model->getGeometry()->getFBXGeometry();
|
opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, args);
|
||||||
for (int i = 0; i < _jointMappings.size(); i++) {
|
|
||||||
int& mapping = _jointMappings[i];
|
|
||||||
if (mapping != -1 && _maskedJoints.contains(geometry.joints.at(mapping).name)) {
|
|
||||||
mapping = -1;
|
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FBXGeometry& animationGeometry = _animation->getGeometry();
|
GLBATCH(glDepthMask)(true);
|
||||||
if (animationGeometry.animationFrames.isEmpty()) {
|
GLBATCH(glDepthFunc)(GL_LESS);
|
||||||
stop();
|
GLBATCH(glDisable)(GL_CULL_FACE);
|
||||||
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) {
|
if (mode == SHADOW_RENDER_MODE) {
|
||||||
_frameIndex = startFrameIndex + glm::mod(_frameIndex - startFrameIndex, endFrameIndex - startFrameIndex);
|
GLBATCH(glCullFace)(GL_BACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
// blend between the closest two frames
|
// deactivate vertex arrays after drawing
|
||||||
applyFrame(_frameIndex);
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationHandle::applyFrame(float frameIndex) {
|
// restore all the default material settings
|
||||||
const FBXGeometry& animationGeometry = _animation->getGeometry();
|
Application::getInstance()->setupWorldLight();
|
||||||
int frameCount = animationGeometry.animationFrames.size();
|
|
||||||
const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount);
|
if (args) {
|
||||||
const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount);
|
args->_translucentMeshPartsRendered = translucentMeshPartsRendered;
|
||||||
float frameFraction = glm::fract(frameIndex);
|
args->_opaqueMeshPartsRendered = opaqueMeshPartsRendered;
|
||||||
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) {
|
void Model::endScene(RenderMode mode, RenderArgs* args) {
|
||||||
for (int i = 0; i < _jointMappings.size(); i++) {
|
//endSceneSimple(mode, args);
|
||||||
int mapping = _jointMappings.at(i);
|
endSceneSplitPass(mode, args);
|
||||||
if (mapping != -1) {
|
|
||||||
JointState& state = _model->_jointStates[mapping];
|
|
||||||
if (_priority == state._animationPriority) {
|
|
||||||
state._animationPriority = newPriority;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <AnimationCache.h>
|
#include <AnimationCache.h>
|
||||||
#include <PhysicsEntity.h>
|
#include <PhysicsEntity.h>
|
||||||
|
|
||||||
|
#include "AnimationHandle.h"
|
||||||
#include "GeometryCache.h"
|
#include "GeometryCache.h"
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
#include "JointState.h"
|
#include "JointState.h"
|
||||||
|
@ -28,14 +29,10 @@
|
||||||
|
|
||||||
class QScriptEngine;
|
class QScriptEngine;
|
||||||
|
|
||||||
class AnimationHandle;
|
|
||||||
class Shape;
|
class Shape;
|
||||||
class RenderArgs;
|
class RenderArgs;
|
||||||
class ViewFrustum;
|
class ViewFrustum;
|
||||||
|
|
||||||
typedef QSharedPointer<AnimationHandle> AnimationHandlePointer;
|
|
||||||
typedef QWeakPointer<AnimationHandle> WeakAnimationHandlePointer;
|
|
||||||
|
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
class Batch;
|
class Batch;
|
||||||
}
|
}
|
||||||
|
@ -93,6 +90,12 @@ public:
|
||||||
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE };
|
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 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.
|
/// 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
|
/// \param fallback the URL of a fallback model to render if the requested model fails to load
|
||||||
|
@ -266,7 +269,6 @@ private:
|
||||||
|
|
||||||
void applyNextGeometry();
|
void applyNextGeometry();
|
||||||
void deleteGeometry();
|
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);
|
QVector<JointState> createJointStates(const FBXGeometry& geometry);
|
||||||
void initJointTransforms();
|
void initJointTransforms();
|
||||||
|
|
||||||
|
@ -394,91 +396,20 @@ private:
|
||||||
QVector<int> _meshesOpaqueTangentsSpecularSkinned;
|
QVector<int> _meshesOpaqueTangentsSpecularSkinned;
|
||||||
QVector<int> _meshesOpaqueSpecularSkinned;
|
QVector<int> _meshesOpaqueSpecularSkinned;
|
||||||
|
|
||||||
|
// Scene rendering support
|
||||||
|
static QVector<Model*> _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<Model>)
|
Q_DECLARE_METATYPE(QPointer<Model>)
|
||||||
Q_DECLARE_METATYPE(QWeakPointer<NetworkGeometry>)
|
Q_DECLARE_METATYPE(QWeakPointer<NetworkGeometry>)
|
||||||
Q_DECLARE_METATYPE(QVector<glm::vec3>)
|
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
|
#endif // hifi_Model_h
|
||||||
|
|
Loading…
Reference in a new issue