mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 15:23:56 +02:00
Merge pull request #2133 from ey6es/master
Delay loading models until we know that we need them (i.e., that we're not going to use the billboard).
This commit is contained in:
commit
348295bbb1
12 changed files with 76 additions and 60 deletions
|
@ -78,7 +78,7 @@ Avatar::Avatar() :
|
|||
_owningAvatarMixer(),
|
||||
_collisionFlags(0),
|
||||
_initialized(false),
|
||||
_billboardHysteresis(false)
|
||||
_shouldRenderBillboard(true)
|
||||
{
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
moveToThread(Application::getInstance()->thread());
|
||||
|
@ -91,11 +91,14 @@ Avatar::Avatar() :
|
|||
Avatar::~Avatar() {
|
||||
}
|
||||
|
||||
const float BILLBOARD_LOD_DISTANCE = 40.0f;
|
||||
|
||||
void Avatar::init() {
|
||||
getHead()->init();
|
||||
getHand()->init();
|
||||
_skeletonModel.init();
|
||||
_initialized = true;
|
||||
_shouldRenderBillboard = (getLODDistance() >= BILLBOARD_LOD_DISTANCE);
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getChestPosition() const {
|
||||
|
@ -117,20 +120,30 @@ void Avatar::simulate(float deltaTime) {
|
|||
setScale(_targetScale);
|
||||
}
|
||||
|
||||
// update the billboard render flag
|
||||
const float BILLBOARD_HYSTERESIS_PROPORTION = 0.1f;
|
||||
if (_shouldRenderBillboard) {
|
||||
if (getLODDistance() < BILLBOARD_LOD_DISTANCE * (1.0f - BILLBOARD_HYSTERESIS_PROPORTION)) {
|
||||
_shouldRenderBillboard = false;
|
||||
}
|
||||
} else if (getLODDistance() > BILLBOARD_LOD_DISTANCE * (1.0f + BILLBOARD_HYSTERESIS_PROPORTION)) {
|
||||
_shouldRenderBillboard = true;
|
||||
}
|
||||
|
||||
// copy velocity so we can use it later for acceleration
|
||||
glm::vec3 oldVelocity = getVelocity();
|
||||
|
||||
getHand()->simulate(deltaTime, false);
|
||||
_skeletonModel.setLODDistance(getLODDistance());
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
Head* head = getHead();
|
||||
_skeletonModel.simulate(deltaTime, _shouldRenderBillboard);
|
||||
glm::vec3 headPosition;
|
||||
if (!_skeletonModel.getHeadPosition(headPosition)) {
|
||||
headPosition = _position;
|
||||
}
|
||||
Head* head = getHead();
|
||||
head->setPosition(headPosition);
|
||||
head->setScale(_scale);
|
||||
getHead()->simulate(deltaTime, false);
|
||||
head->simulate(deltaTime, false, _shouldRenderBillboard);
|
||||
|
||||
// use speed and angular velocity to determine walking vs. standing
|
||||
if (_speed + fabs(_bodyYawDelta) > 0.2) {
|
||||
|
@ -291,22 +304,10 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
|||
return glm::angleAxis(angle * proportion, axis);
|
||||
}
|
||||
|
||||
const float BILLBOARD_LOD_DISTANCE = 40.0f;
|
||||
|
||||
void Avatar::renderBody() {
|
||||
if (!_billboard.isEmpty()) {
|
||||
const float BILLBOARD_HYSTERESIS_PROPORTION = 0.1f;
|
||||
if (_billboardHysteresis) {
|
||||
if (getLODDistance() < BILLBOARD_LOD_DISTANCE * (1.0f - BILLBOARD_HYSTERESIS_PROPORTION)) {
|
||||
_billboardHysteresis = false;
|
||||
}
|
||||
} else if (getLODDistance() > BILLBOARD_LOD_DISTANCE * (1.0f + BILLBOARD_HYSTERESIS_PROPORTION)) {
|
||||
_billboardHysteresis = true;
|
||||
}
|
||||
if (_billboardHysteresis) {
|
||||
renderBillboard();
|
||||
return;
|
||||
}
|
||||
if (_shouldRenderBillboard) {
|
||||
renderBillboard();
|
||||
return;
|
||||
}
|
||||
_skeletonModel.render(1.0f);
|
||||
getHead()->render(1.0f);
|
||||
|
@ -314,9 +315,14 @@ void Avatar::renderBody() {
|
|||
}
|
||||
|
||||
void Avatar::renderBillboard() {
|
||||
if (_billboard.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!_billboardTexture) {
|
||||
QImage image = QImage::fromData(_billboard).convertToFormat(QImage::Format_ARGB32);
|
||||
|
||||
QImage image = QImage::fromData(_billboard);
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
_billboardTexture.reset(new Texture());
|
||||
glBindTexture(GL_TEXTURE_2D, _billboardTexture->getID());
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1,
|
||||
|
@ -555,16 +561,16 @@ bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float parti
|
|||
return collided;
|
||||
}
|
||||
|
||||
void Avatar::setFaceModelURL(const QUrl &faceModelURL) {
|
||||
void Avatar::setFaceModelURL(const QUrl& faceModelURL) {
|
||||
AvatarData::setFaceModelURL(faceModelURL);
|
||||
const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_head.fst");
|
||||
getHead()->getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL);
|
||||
getHead()->getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL, !isMyAvatar());
|
||||
}
|
||||
|
||||
void Avatar::setSkeletonModelURL(const QUrl &skeletonModelURL) {
|
||||
void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||
AvatarData::setSkeletonModelURL(skeletonModelURL);
|
||||
const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_body.fst");
|
||||
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL);
|
||||
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, !isMyAvatar());
|
||||
}
|
||||
|
||||
void Avatar::setDisplayName(const QString& displayName) {
|
||||
|
@ -577,9 +583,6 @@ void Avatar::setBillboard(const QByteArray& billboard) {
|
|||
|
||||
// clear out any existing billboard texture
|
||||
_billboardTexture.reset();
|
||||
|
||||
// reset the hysteresis value
|
||||
_billboardHysteresis = (getLODDistance() >= BILLBOARD_LOD_DISTANCE);
|
||||
}
|
||||
|
||||
int Avatar::parseData(const QByteArray& packet) {
|
||||
|
|
|
@ -177,7 +177,7 @@ private:
|
|||
|
||||
bool _initialized;
|
||||
QScopedPointer<Texture> _billboardTexture;
|
||||
bool _billboardHysteresis;
|
||||
bool _shouldRenderBillboard;
|
||||
|
||||
void renderBody();
|
||||
void renderBillboard();
|
||||
|
|
|
@ -18,9 +18,9 @@ FaceModel::FaceModel(Head* owningHead) :
|
|||
{
|
||||
}
|
||||
|
||||
void FaceModel::simulate(float deltaTime) {
|
||||
void FaceModel::simulate(float deltaTime, bool delayLoad) {
|
||||
if (!isActive()) {
|
||||
Model::simulate(deltaTime);
|
||||
Model::simulate(deltaTime, delayLoad);
|
||||
return;
|
||||
}
|
||||
Avatar* owningAvatar = static_cast<Avatar*>(_owningHead->_owningAvatar);
|
||||
|
@ -41,7 +41,7 @@ void FaceModel::simulate(float deltaTime) {
|
|||
setPupilDilation(_owningHead->getPupilDilation());
|
||||
setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients());
|
||||
|
||||
Model::simulate(deltaTime);
|
||||
Model::simulate(deltaTime, delayLoad);
|
||||
}
|
||||
|
||||
bool FaceModel::render(float alpha) {
|
||||
|
|
|
@ -21,7 +21,7 @@ public:
|
|||
|
||||
FaceModel(Head* owningHead);
|
||||
|
||||
void simulate(float deltaTime);
|
||||
void simulate(float deltaTime, bool delayLoad = false);
|
||||
bool render(float alpha);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -58,7 +58,7 @@ void Head::reset() {
|
|||
|
||||
|
||||
|
||||
void Head::simulate(float deltaTime, bool isMine) {
|
||||
void Head::simulate(float deltaTime, bool isMine, bool delayLoad) {
|
||||
|
||||
// Update audio trailing average for rendering facial animations
|
||||
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
||||
|
@ -161,7 +161,7 @@ void Head::simulate(float deltaTime, bool isMine) {
|
|||
if (!isMine) {
|
||||
_faceModel.setLODDistance(static_cast<Avatar*>(_owningAvatar)->getLODDistance());
|
||||
}
|
||||
_faceModel.simulate(deltaTime);
|
||||
_faceModel.simulate(deltaTime, delayLoad);
|
||||
|
||||
// the blend face may have custom eye meshes
|
||||
if (!_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition)) {
|
||||
|
|
|
@ -36,7 +36,7 @@ public:
|
|||
|
||||
void init();
|
||||
void reset();
|
||||
void simulate(float deltaTime, bool isMine);
|
||||
void simulate(float deltaTime, bool isMine, bool delayLoad = false);
|
||||
void render(float alpha);
|
||||
void setScale(float scale);
|
||||
void setPosition(glm::vec3 position) { _position = position; }
|
||||
|
|
|
@ -18,9 +18,9 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) :
|
|||
_owningAvatar(owningAvatar) {
|
||||
}
|
||||
|
||||
void SkeletonModel::simulate(float deltaTime) {
|
||||
void SkeletonModel::simulate(float deltaTime, bool delayLoad) {
|
||||
if (!isActive()) {
|
||||
Model::simulate(deltaTime);
|
||||
Model::simulate(deltaTime, delayLoad);
|
||||
return;
|
||||
}
|
||||
setTranslation(_owningAvatar->getPosition());
|
||||
|
@ -28,7 +28,7 @@ void SkeletonModel::simulate(float deltaTime) {
|
|||
const float MODEL_SCALE = 0.0006f;
|
||||
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale() * MODEL_SCALE);
|
||||
|
||||
Model::simulate(deltaTime);
|
||||
Model::simulate(deltaTime, delayLoad);
|
||||
|
||||
// find the left and rightmost active Leap palms
|
||||
int leftPalmIndex, rightPalmIndex;
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
|
||||
SkeletonModel(Avatar* owningAvatar);
|
||||
|
||||
void simulate(float deltaTime);
|
||||
void simulate(float deltaTime, bool delayLoad = false);
|
||||
bool render(float alpha);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -286,14 +286,14 @@ void GeometryCache::renderGrid(int xDivisions, int yDivisions) {
|
|||
buffer.release();
|
||||
}
|
||||
|
||||
QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback) {
|
||||
QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad) {
|
||||
if (!url.isValid() && fallback.isValid()) {
|
||||
return getGeometry(fallback);
|
||||
return getGeometry(fallback, QUrl(), delayLoad);
|
||||
}
|
||||
QSharedPointer<NetworkGeometry> geometry = _networkGeometry.value(url);
|
||||
if (geometry.isNull()) {
|
||||
geometry = QSharedPointer<NetworkGeometry>(new NetworkGeometry(url, fallback.isValid() ?
|
||||
getGeometry(fallback) : QSharedPointer<NetworkGeometry>()));
|
||||
getGeometry(fallback, QUrl(), true) : QSharedPointer<NetworkGeometry>(), delayLoad));
|
||||
geometry->setLODParent(geometry);
|
||||
_networkGeometry.insert(url, geometry);
|
||||
}
|
||||
|
@ -302,7 +302,7 @@ QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, cons
|
|||
|
||||
const float NetworkGeometry::NO_HYSTERESIS = -1.0f;
|
||||
|
||||
NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback,
|
||||
NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback, bool delayLoad,
|
||||
const QVariantHash& mapping, const QUrl& textureBase) :
|
||||
_request(url),
|
||||
_reply(NULL),
|
||||
|
@ -318,8 +318,8 @@ NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGe
|
|||
}
|
||||
_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||
|
||||
// if we already have a mapping (because we're an LOD), hold off on loading until we're requested
|
||||
if (mapping.isEmpty()) {
|
||||
// start loading immediately unless instructed otherwise
|
||||
if (!delayLoad) {
|
||||
makeRequest();
|
||||
}
|
||||
}
|
||||
|
@ -330,9 +330,15 @@ NetworkGeometry::~NetworkGeometry() {
|
|||
}
|
||||
}
|
||||
|
||||
QSharedPointer<NetworkGeometry> NetworkGeometry::getLODOrFallback(float distance, float& hysteresis) const {
|
||||
void NetworkGeometry::ensureLoading() {
|
||||
if (!_startedLoading) {
|
||||
makeRequest();
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<NetworkGeometry> NetworkGeometry::getLODOrFallback(float distance, float& hysteresis, bool delayLoad) const {
|
||||
if (_lodParent.data() != this) {
|
||||
return _lodParent.data()->getLODOrFallback(distance, hysteresis);
|
||||
return _lodParent.data()->getLODOrFallback(distance, hysteresis, delayLoad);
|
||||
}
|
||||
if (_failedToLoad && _fallback) {
|
||||
return _fallback;
|
||||
|
@ -357,8 +363,8 @@ QSharedPointer<NetworkGeometry> NetworkGeometry::getLODOrFallback(float distance
|
|||
return lod;
|
||||
}
|
||||
// if the ideal LOD isn't loaded, we need to make sure it's started to load, and possibly return the closest loaded one
|
||||
if (!lod->_startedLoading) {
|
||||
lod->makeRequest();
|
||||
if (!delayLoad) {
|
||||
lod->ensureLoading();
|
||||
}
|
||||
float closestDistance = FLT_MAX;
|
||||
if (isLoaded()) {
|
||||
|
@ -438,7 +444,7 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
|||
QVariantHash lods = _mapping.value("lod").toHash();
|
||||
for (QVariantHash::const_iterator it = lods.begin(); it != lods.end(); it++) {
|
||||
QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url.resolved(it.key()),
|
||||
QSharedPointer<NetworkGeometry>(), _mapping, _textureBase));
|
||||
QSharedPointer<NetworkGeometry>(), true, _mapping, _textureBase));
|
||||
geometry->setLODParent(_lodParent);
|
||||
_lods.insert(it.value().toFloat(), geometry);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,8 @@ public:
|
|||
|
||||
/// Loads geometry from the specified URL.
|
||||
/// \param fallback a fallback URL to load if the desired one is unavailable
|
||||
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl());
|
||||
/// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested
|
||||
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -65,16 +66,19 @@ public:
|
|||
/// A hysteresis value indicating that we have no state memory.
|
||||
static const float NO_HYSTERESIS;
|
||||
|
||||
NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback,
|
||||
NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback, bool delayLoad,
|
||||
const QVariantHash& mapping = QVariantHash(), const QUrl& textureBase = QUrl());
|
||||
~NetworkGeometry();
|
||||
|
||||
/// Checks whether the geometry is fulled loaded.
|
||||
bool isLoaded() const { return !_geometry.joints.isEmpty(); }
|
||||
|
||||
/// Makes sure that the geometry has started loading.
|
||||
void ensureLoading();
|
||||
|
||||
/// Returns a pointer to the geometry appropriate for the specified distance.
|
||||
/// \param hysteresis a hysteresis parameter that prevents rapid model switching
|
||||
QSharedPointer<NetworkGeometry> getLODOrFallback(float distance, float& hysteresis) const;
|
||||
QSharedPointer<NetworkGeometry> getLODOrFallback(float distance, float& hysteresis, bool delayLoad = false) const;
|
||||
|
||||
const FBXGeometry& getFBXGeometry() const { return _geometry; }
|
||||
const QVector<NetworkMesh>& getMeshes() const { return _meshes; }
|
||||
|
|
|
@ -115,11 +115,11 @@ void Model::reset() {
|
|||
}
|
||||
}
|
||||
|
||||
void Model::simulate(float deltaTime) {
|
||||
void Model::simulate(float deltaTime, bool delayLoad) {
|
||||
// update our LOD
|
||||
QVector<JointState> newJointStates;
|
||||
if (_geometry) {
|
||||
QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis);
|
||||
QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad);
|
||||
if (_geometry != geometry) {
|
||||
if (!_jointStates.isEmpty()) {
|
||||
// copy the existing joint states
|
||||
|
@ -138,6 +138,9 @@ void Model::simulate(float deltaTime) {
|
|||
_dilatedTextures.clear();
|
||||
_geometry = geometry;
|
||||
}
|
||||
if (!delayLoad) {
|
||||
_geometry->ensureLoading();
|
||||
}
|
||||
}
|
||||
if (!isActive()) {
|
||||
return;
|
||||
|
@ -443,7 +446,7 @@ float Model::getRightArmLength() const {
|
|||
return getLimbLength(getRightHandJointIndex());
|
||||
}
|
||||
|
||||
void Model::setURL(const QUrl& url, const QUrl& fallback) {
|
||||
void Model::setURL(const QUrl& url, const QUrl& fallback, bool delayLoad) {
|
||||
// don't recreate the geometry if it's the same URL
|
||||
if (_url == url) {
|
||||
return;
|
||||
|
@ -456,7 +459,7 @@ void Model::setURL(const QUrl& url, const QUrl& fallback) {
|
|||
_lodHysteresis = NetworkGeometry::NO_HYSTERESIS;
|
||||
|
||||
// we retain a reference to the base geometry so that its reference count doesn't fall to zero
|
||||
_baseGeometry = _geometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback);
|
||||
_baseGeometry = _geometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback, delayLoad);
|
||||
}
|
||||
|
||||
glm::vec4 Model::computeAverageColor() const {
|
||||
|
|
|
@ -50,10 +50,10 @@ public:
|
|||
|
||||
void init();
|
||||
void reset();
|
||||
void simulate(float deltaTime);
|
||||
void simulate(float deltaTime, bool delayLoad = false);
|
||||
bool render(float alpha);
|
||||
|
||||
Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl());
|
||||
Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
|
||||
const QUrl& getURL() const { return _url; }
|
||||
|
||||
/// Sets the distance parameter used for LOD computations.
|
||||
|
|
Loading…
Reference in a new issue