//// 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 <gpu/GPUConfig.h> #include <QMetaType>#include <QRunnable>#include <QThreadPool> #include <glm/gtx/transform.hpp>#include <glm/gtx/norm.hpp> #include <CapsuleShape.h>#include <GeometryUtil.h>#include <gpu/Batch.h>#include <gpu/GLBackend.h>#include <PathUtils.h>#include <PerfStat.h>#include <PhysicsEntity.h>#include <ShapeCollider.h>#include <SphereShape.h>#include <ViewFrustum.h> #include "AbstractViewStateInterface.h"#include "AnimationHandle.h"#include "DeferredLightingEffect.h"#include "GlowEffect.h"#include "Model.h" #include "model_vert.h"#include "model_shadow_vert.h"
#include "model_normal_map_vert.h"
#include "model_lightmap_vert.h"
#include "model_lightmap_normal_map_vert.h"
#include "skin_model_vert.h"
#include "skin_model_shadow_vert.h"
#include "skin_model_normal_map_vert.h"
#include "model_frag.h"#include "model_shadow_frag.h"#include "model_normal_map_frag.h"#include "model_normal_specular_map_frag.h"#include "model_specular_map_frag.h"#include "model_lightmap_frag.h"#include "model_lightmap_normal_map_frag.h"#include "model_lightmap_normal_specular_map_frag.h"#include "model_lightmap_specular_map_frag.h"#include "model_translucent_frag.h" #define GLBATCH( call ) batch._##call//#define GLBATCH( call ) call using namespace std; static int modelPointerTypeId = qRegisterMetaType<QPointer<Model> >();static int weakNetworkGeometryPointerTypeId = qRegisterMetaType<QWeakPointer<NetworkGeometry> >();static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; Model::Model(QObject* parent) : QObject(parent), _scale(1.0f, 1.0f, 1.0f), _scaleToFit(false), _scaleToFitDimensions(0.0f), _scaledToFit(false), _snapModelToRegistrationPoint(false), _snappedToRegistrationPoint(false), _showTrueJointTransforms(true), _lodDistance(0.0f), _pupilDilation(0.0f), _url("http://invalid.com"), _blendNumber(0), _appliedBlendNumber(0), _calculatedMeshBoxesValid(false), _calculatedMeshTrianglesValid(false), _meshGroupsKnown(false) { // we may have been created in the network thread, but we live in the main thread if (_viewState) { moveToThread(_viewState->getMainThread()); }} Model::~Model() { deleteGeometry();} ProgramObject Model::_program;ProgramObject Model::_normalMapProgram;ProgramObject Model::_specularMapProgram;ProgramObject Model::_normalSpecularMapProgram;ProgramObject Model::_translucentProgram; ProgramObject Model::_lightmapProgram;ProgramObject Model::_lightmapNormalMapProgram;ProgramObject Model::_lightmapSpecularMapProgram;ProgramObject Model::_lightmapNormalSpecularMapProgram; ProgramObject Model::_shadowProgram; ProgramObject Model::_skinProgram;ProgramObject Model::_skinNormalMapProgram;ProgramObject Model::_skinSpecularMapProgram;ProgramObject Model::_skinNormalSpecularMapProgram;ProgramObject Model::_skinTranslucentProgram; ProgramObject Model::_skinShadowProgram; Model::Locations Model::_locations;Model::Locations Model::_normalMapLocations;Model::Locations Model::_specularMapLocations;Model::Locations Model::_normalSpecularMapLocations;Model::Locations Model::_translucentLocations; Model::Locations Model::_lightmapLocations;Model::Locations Model::_lightmapNormalMapLocations;Model::Locations Model::_lightmapSpecularMapLocations;Model::Locations Model::_lightmapNormalSpecularMapLocations; Model::SkinLocations Model::_skinLocations;Model::SkinLocations Model::_skinNormalMapLocations;Model::SkinLocations Model::_skinSpecularMapLocations;Model::SkinLocations Model::_skinNormalSpecularMapLocations;Model::SkinLocations Model::_skinShadowLocations;Model::SkinLocations Model::_skinTranslucentLocations; AbstractViewStateInterface* Model::_viewState = NULL; void Model::setScale(const glm::vec3& scale) { setScaleInternal(scale); // if anyone sets scale manually, then we are no longer scaled to fit _scaleToFit = false; _scaledToFit = false;} void Model::setScaleInternal(const glm::vec3& scale) { float scaleLength = glm::length(_scale); float relativeDeltaScale = glm::length(_scale - scale) / scaleLength; const float ONE_PERCENT = 0.01f; if (relativeDeltaScale > ONE_PERCENT || scaleLength < EPSILON) { _scale = scale; initJointTransforms(); if (_shapes.size() > 0) { clearShapes(); buildShapes(); } }} void Model::setOffset(const glm::vec3& offset) { _offset = offset; // if someone manually sets our offset, then we are no longer snapped to center _snapModelToRegistrationPoint = false; _snappedToRegistrationPoint = false; } void Model::initProgram(ProgramObject& program, Model::Locations& locations, bool link) { if (link) { program.bindAttributeLocation("tangent", gpu::Stream::TANGENT); program.bindAttributeLocation("texcoord1", gpu::Stream::TEXCOORD1); program.link(); } program.bind(); locations.tangent = program.attributeLocation("tangent"); locations.alphaThreshold = program.uniformLocation("alphaThreshold"); locations.texcoordMatrices = program.uniformLocation("texcoordMatrices"); locations.emissiveParams = program.uniformLocation("emissiveParams"); locations.glowIntensity = program.uniformLocation("glowIntensity"); program.setUniformValue("diffuseMap", 0); program.setUniformValue("normalMap", 1); int loc = program.uniformLocation("specularMap"); if (loc >= 0) { program.setUniformValue("specularMap", 2); locations.specularTextureUnit = 2; } else { locations.specularTextureUnit = -1; } loc = program.uniformLocation("emissiveMap"); if (loc >= 0) { program.setUniformValue("emissiveMap", 3); locations.emissiveTextureUnit = 3; } else { locations.emissiveTextureUnit = -1; } // bindable uniform version
#if defined(Q_OS_MAC)
loc = program.uniformLocation("materialBuffer");
if (loc >= 0) {
locations.materialBufferUnit = loc;
} else {
locations.materialBufferUnit = -1;
}
#elif defined(Q_OS_WIN)
loc = glGetUniformBlockIndex(program.programId(), "materialBuffer");
if (loc >= 0) {
glUniformBlockBinding(program.programId(), loc, 1);
locations.materialBufferUnit = 1;
} else {
locations.materialBufferUnit = -1;
}
#else
loc = program.uniformLocation("materialBuffer");
if (loc >= 0) {
locations.materialBufferUnit = loc;
} else {
locations.materialBufferUnit = -1;
}
#endif
if (!program.isLinked()) {
program.release();
}
program.release();} void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations) { program.bindAttributeLocation("tangent", gpu::Stream::TANGENT); program.bindAttributeLocation("texcoord1", gpu::Stream::TEXCOORD1); program.bindAttributeLocation("clusterIndices", gpu::Stream::SKIN_CLUSTER_INDEX); program.bindAttributeLocation("clusterWeights", gpu::Stream::SKIN_CLUSTER_WEIGHT); program.link(); initProgram(program, locations, false); program.bind(); locations.clusterMatrices = program.uniformLocation("clusterMatrices"); locations.clusterIndices = program.attributeLocation("clusterIndices"); locations.clusterWeights = program.attributeLocation("clusterWeights"); program.release();} QVector<JointState> Model::createJointStates(const FBXGeometry& geometry) { QVector<JointState> jointStates; for (int i = 0; i < geometry.joints.size(); ++i) { const FBXJoint& joint = geometry.joints[i]; // store a pointer to the FBXJoint in the JointState JointState state; state.setFBXJoint(&joint); jointStates.append(state); } return jointStates;}; void Model::initJointTransforms() { // compute model transforms int numStates = _jointStates.size(); for (int i = 0; i < numStates; ++i) { JointState& state = _jointStates[i]; const FBXJoint& joint = state.getFBXJoint(); int parentIndex = joint.parentIndex; if (parentIndex == -1) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; state.initTransform(parentTransform); } else { const JointState& parentState = _jointStates.at(parentIndex); state.initTransform(parentState.getTransform()); } }} void Model::init() { if (!_program.isLinked()) { _program.addShaderFromSourceCode(QGLShader::Vertex, model_vert); _program.addShaderFromSourceCode(QGLShader::Fragment, model_frag); initProgram(_program, _locations); _normalMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_normal_map_vert);
_normalMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_normal_map_frag);
initProgram(_normalMapProgram, _normalMapLocations);
_specularMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_vert);
_specularMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_specular_map_frag);
initProgram(_specularMapProgram, _specularMapLocations);
_normalSpecularMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_normal_map_vert);
_normalSpecularMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_normal_specular_map_frag);
initProgram(_normalSpecularMapProgram, _normalSpecularMapLocations);
_translucentProgram.addShaderFromSourceCode(QGLShader::Vertex, model_vert);
_translucentProgram.addShaderFromSourceCode(QGLShader::Fragment, model_translucent_frag);
initProgram(_translucentProgram, _translucentLocations);
// Lightmap
_lightmapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_lightmap_vert);
_lightmapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_lightmap_frag);
initProgram(_lightmapProgram, _lightmapLocations);
_lightmapNormalMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_lightmap_normal_map_vert);
_lightmapNormalMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_lightmap_normal_map_frag);
initProgram(_lightmapNormalMapProgram, _lightmapNormalMapLocations);
_lightmapSpecularMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_lightmap_vert);
_lightmapSpecularMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_lightmap_specular_map_frag);
initProgram(_lightmapSpecularMapProgram, _lightmapSpecularMapLocations);
_lightmapNormalSpecularMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_lightmap_normal_map_vert);
_lightmapNormalSpecularMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_lightmap_normal_specular_map_frag);
initProgram(_lightmapNormalSpecularMapProgram, _lightmapNormalSpecularMapLocations);
// end lightmap
_shadowProgram.addShaderFromSourceCode(QGLShader::Vertex, model_shadow_vert);
_shadowProgram.addShaderFromSourceCode(QGLShader::Fragment, model_shadow_frag);
_skinProgram.addShaderFromSourceCode(QGLShader::Vertex, skin_model_vert);
_skinProgram.addShaderFromSourceCode(QGLShader::Fragment, model_frag);
initSkinProgram(_skinProgram, _skinLocations);
_skinNormalMapProgram.addShaderFromSourceCode(QGLShader::Vertex, skin_model_normal_map_vert);
_skinNormalMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_normal_map_frag);
initSkinProgram(_skinNormalMapProgram, _skinNormalMapLocations);
_skinSpecularMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_vert);
_skinSpecularMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_specular_map_frag);
initSkinProgram(_skinSpecularMapProgram, _skinSpecularMapLocations);
_skinNormalSpecularMapProgram.addShaderFromSourceCode(QGLShader::Vertex, skin_model_normal_map_vert);
_skinNormalSpecularMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_normal_specular_map_frag);
initSkinProgram(_skinNormalSpecularMapProgram, _skinNormalSpecularMapLocations);
_skinShadowProgram.addShaderFromSourceCode(QGLShader::Vertex, skin_model_shadow_vert);
_skinShadowProgram.addShaderFromSourceCode(QGLShader::Fragment, model_shadow_frag);
initSkinProgram(_skinShadowProgram, _skinShadowLocations);
_skinTranslucentProgram.addShaderFromSourceCode(QGLShader::Vertex, skin_model_vert);
_skinTranslucentProgram.addShaderFromSourceCode(QGLShader::Fragment, model_translucent_frag);
initSkinProgram(_skinTranslucentProgram, _skinTranslucentLocations); }} void Model::reset() { if (_jointStates.isEmpty()) { return; } foreach (Model* attachment, _attachments) { attachment->reset(); } const FBXGeometry& geometry = _geometry->getFBXGeometry(); for (int i = 0; i < _jointStates.size(); i++) { _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f); } _meshGroupsKnown = false;} bool Model::updateGeometry() { // NOTE: this is a recursive call that walks all attachments, and their attachments bool needFullUpdate = false; for (int i = 0; i < _attachments.size(); i++) { Model* model = _attachments.at(i); if (model->updateGeometry()) { needFullUpdate = true; } } bool needToRebuild = false; if (_nextGeometry) { _nextGeometry = _nextGeometry->getLODOrFallback(_lodDistance, _nextLODHysteresis); _nextGeometry->setLoadPriority(this, -_lodDistance); _nextGeometry->ensureLoading(); if (_nextGeometry->isLoaded()) { applyNextGeometry(); needToRebuild = true; } } if (!_geometry) { // geometry is not ready return false; } QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis); if (_geometry != geometry) { // NOTE: it is theoretically impossible to reach here after passing through the applyNextGeometry() call above. // Which means we don't need to worry about calling deleteGeometry() below immediately after creating new geometry. const FBXGeometry& newGeometry = geometry->getFBXGeometry(); QVector<JointState> newJointStates = createJointStates(newGeometry); if (! _jointStates.isEmpty()) { // copy the existing joint states const FBXGeometry& oldGeometry = _geometry->getFBXGeometry(); for (QHash<QString, int>::const_iterator it = oldGeometry.jointIndices.constBegin(); it != oldGeometry.jointIndices.constEnd(); it++) { int oldIndex = it.value() - 1; int newIndex = newGeometry.getJointIndex(it.key()); if (newIndex != -1) { newJointStates[newIndex].copyState(_jointStates[oldIndex]); } } } deleteGeometry(); _dilatedTextures.clear(); _geometry = geometry; _meshGroupsKnown = false; setJointStates(newJointStates); needToRebuild = true; } else if (_jointStates.isEmpty()) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); if (fbxGeometry.joints.size() > 0) { setJointStates(createJointStates(fbxGeometry)); needToRebuild = true; } } else if (!geometry->isLoaded()) { deleteGeometry(); _dilatedTextures.clear(); } _geometry->setLoadPriority(this, -_lodDistance); _geometry->ensureLoading(); if (needToRebuild) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterMatrices.resize(mesh.clusters.size()); _meshStates.append(state); gpu::BufferPointer buffer(new gpu::Buffer()); if (!mesh.blendshapes.isEmpty()) { buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3)); buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.vertices.constData()); buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), mesh.normals.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.normals.constData()); } _blendedVertexBuffers.push_back(buffer); } foreach (const FBXAttachment& attachment, fbxGeometry.attachments) { Model* model = new Model(this); model->init(); model->setURL(attachment.url); _attachments.append(model); } needFullUpdate = true; } return needFullUpdate;} // virtualvoid Model::setJointStates(QVector<JointState> states) { _jointStates = states; initJointTransforms(); int numStates = _jointStates.size(); float radius = 0.0f; for (int i = 0; i < numStates; ++i) { float distance = glm::length(_jointStates[i].getPosition()); if (distance > radius) { radius = distance; } _jointStates[i].buildConstraint(); } for (int i = 0; i < _jointStates.size(); i++) { _jointStates[i].slaveVisibleTransform(); } _boundingRadius = radius;} bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, QString& extraInfo, bool pickAgainstTriangles) { bool intersectedSomething = false; // if we aren't active, we can't ray pick yet... if (!isActive()) { return intersectedSomething; } // extents is the entity relative, scaled, centered extents of the entity glm::vec3 position = _translation; glm::mat4 rotation = glm::mat4_cast(_rotation); glm::mat4 translation = glm::translate(position); glm::mat4 modelToWorldMatrix = translation * rotation; glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix); Extents modelExtents = getMeshExtents(); // NOTE: unrotated glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum; glm::vec3 corner = -(dimensions * _registrationPoint); // since we're going to do the ray picking in the model frame of reference AABox modelFrameBox(corner, dimensions);Lines 1–500 of 2549. Load more
// // 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 <gpu/GPUConfig.h> #include <QMetaType> #include <QRunnable> #include <QThreadPool> #include <glm/gtx/transform.hpp> #include <glm/gtx/norm.hpp> #include <CapsuleShape.h> #include <GeometryUtil.h> #include <gpu/Batch.h> #include <gpu/GLBackend.h> #include <PathUtils.h> #include <PerfStat.h> #include <PhysicsEntity.h> #include <ShapeCollider.h> #include <SphereShape.h> #include <ViewFrustum.h> #include "AbstractViewStateInterface.h" #include "AnimationHandle.h" #include "DeferredLightingEffect.h" #include "GlowEffect.h" #include "Model.h" #include "model_vert.h" #include "model_shadow_vert.h" #include "model_normal_map_vert.h" #include "model_lightmap_vert.h" #include "model_lightmap_normal_map_vert.h" #include "skin_model_vert.h" #include "skin_model_shadow_vert.h" #include "skin_model_normal_map_vert.h" #include "model_frag.h" #include "model_shadow_frag.h" #include "model_normal_map_frag.h" #include "model_normal_specular_map_frag.h" #include "model_specular_map_frag.h" #include "model_lightmap_frag.h" #include "model_lightmap_normal_map_frag.h" #include "model_lightmap_normal_specular_map_frag.h" #include "model_lightmap_specular_map_frag.h" #include "model_translucent_frag.h" #define GLBATCH( call ) batch._##call //#define GLBATCH( call ) call using namespace std; static int modelPointerTypeId = qRegisterMetaType<QPointer<Model> >(); static int weakNetworkGeometryPointerTypeId = qRegisterMetaType<QWeakPointer<NetworkGeometry> >(); static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >(); float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; Model::Model(QObject* parent) : QObject(parent), _scale(1.0f, 1.0f, 1.0f), _scaleToFit(false), _scaleToFitDimensions(0.0f), _scaledToFit(false), _snapModelToRegistrationPoint(false), _snappedToRegistrationPoint(false), _showTrueJointTransforms(true), _lodDistance(0.0f), _pupilDilation(0.0f), _url("http://invalid.com"), _blendNumber(0), _appliedBlendNumber(0), _calculatedMeshBoxesValid(false), _calculatedMeshTrianglesValid(false), _meshGroupsKnown(false) { // we may have been created in the network thread, but we live in the main thread if (_viewState) { moveToThread(_viewState->getMainThread()); } } Model::~Model() { deleteGeometry(); } ProgramObject Model::_program; ProgramObject Model::_normalMapProgram; ProgramObject Model::_specularMapProgram; ProgramObject Model::_normalSpecularMapProgram; ProgramObject Model::_translucentProgram; ProgramObject Model::_lightmapProgram; ProgramObject Model::_lightmapNormalMapProgram; ProgramObject Model::_lightmapSpecularMapProgram; ProgramObject Model::_lightmapNormalSpecularMapProgram; ProgramObject Model::_shadowProgram; ProgramObject Model::_skinProgram; ProgramObject Model::_skinNormalMapProgram; ProgramObject Model::_skinSpecularMapProgram; ProgramObject Model::_skinNormalSpecularMapProgram; ProgramObject Model::_skinTranslucentProgram; ProgramObject Model::_skinShadowProgram; Model::Locations Model::_locations; Model::Locations Model::_normalMapLocations; Model::Locations Model::_specularMapLocations; Model::Locations Model::_normalSpecularMapLocations; Model::Locations Model::_translucentLocations; Model::Locations Model::_lightmapLocations; Model::Locations Model::_lightmapNormalMapLocations; Model::Locations Model::_lightmapSpecularMapLocations; Model::Locations Model::_lightmapNormalSpecularMapLocations; Model::SkinLocations Model::_skinLocations; Model::SkinLocations Model::_skinNormalMapLocations; Model::SkinLocations Model::_skinSpecularMapLocations; Model::SkinLocations Model::_skinNormalSpecularMapLocations; Model::SkinLocations Model::_skinShadowLocations; Model::SkinLocations Model::_skinTranslucentLocations; AbstractViewStateInterface* Model::_viewState = NULL; void Model::setScale(const glm::vec3& scale) { setScaleInternal(scale); // if anyone sets scale manually, then we are no longer scaled to fit _scaleToFit = false; _scaledToFit = false; } void Model::setScaleInternal(const glm::vec3& scale) { float scaleLength = glm::length(_scale); float relativeDeltaScale = glm::length(_scale - scale) / scaleLength; const float ONE_PERCENT = 0.01f; if (relativeDeltaScale > ONE_PERCENT || scaleLength < EPSILON) { _scale = scale; initJointTransforms(); if (_shapes.size() > 0) { clearShapes(); buildShapes(); } } } void Model::setOffset(const glm::vec3& offset) { _offset = offset; // if someone manually sets our offset, then we are no longer snapped to center _snapModelToRegistrationPoint = false; _snappedToRegistrationPoint = false; } void Model::initProgram(ProgramObject& program, Model::Locations& locations, bool link) { if (link) { program.bindAttributeLocation("tangent", gpu::Stream::TANGENT); program.bindAttributeLocation("texcoord1", gpu::Stream::TEXCOORD1); program.link(); } program.bind(); locations.tangent = program.attributeLocation("tangent"); locations.alphaThreshold = program.uniformLocation("alphaThreshold"); locations.texcoordMatrices = program.uniformLocation("texcoordMatrices"); locations.emissiveParams = program.uniformLocation("emissiveParams"); locations.glowIntensity = program.uniformLocation("glowIntensity"); program.setUniformValue("diffuseMap", 0); program.setUniformValue("normalMap", 1); int loc = program.uniformLocation("specularMap"); if (loc >= 0) { program.setUniformValue("specularMap", 2); locations.specularTextureUnit = 2; } else { locations.specularTextureUnit = -1; } loc = program.uniformLocation("emissiveMap"); if (loc >= 0) { program.setUniformValue("emissiveMap", 3); locations.emissiveTextureUnit = 3; } else { locations.emissiveTextureUnit = -1; } // bindable uniform version #if defined(Q_OS_MAC) loc = program.uniformLocation("materialBuffer"); if (loc >= 0) { locations.materialBufferUnit = loc; } else { locations.materialBufferUnit = -1; } #elif defined(Q_OS_WIN) loc = glGetUniformBlockIndex(program.programId(), "materialBuffer"); if (loc >= 0) { glUniformBlockBinding(program.programId(), loc, 1); locations.materialBufferUnit = 1; } else { locations.materialBufferUnit = -1; } #else loc = program.uniformLocation("materialBuffer"); if (loc >= 0) { locations.materialBufferUnit = loc; } else { locations.materialBufferUnit = -1; } #endif if (!program.isLinked()) { program.release(); } program.release(); } void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations) { program.bindAttributeLocation("tangent", gpu::Stream::TANGENT); program.bindAttributeLocation("texcoord1", gpu::Stream::TEXCOORD1); program.bindAttributeLocation("clusterIndices", gpu::Stream::SKIN_CLUSTER_INDEX); program.bindAttributeLocation("clusterWeights", gpu::Stream::SKIN_CLUSTER_WEIGHT); program.link(); initProgram(program, locations, false); program.bind(); locations.clusterMatrices = program.uniformLocation("clusterMatrices"); locations.clusterIndices = program.attributeLocation("clusterIndices"); locations.clusterWeights = program.attributeLocation("clusterWeights"); program.release(); } QVector<JointState> Model::createJointStates(const FBXGeometry& geometry) { QVector<JointState> jointStates; for (int i = 0; i < geometry.joints.size(); ++i) { const FBXJoint& joint = geometry.joints[i]; // store a pointer to the FBXJoint in the JointState JointState state; state.setFBXJoint(&joint); jointStates.append(state); } return jointStates; }; void Model::initJointTransforms() { // compute model transforms int numStates = _jointStates.size(); for (int i = 0; i < numStates; ++i) { JointState& state = _jointStates[i]; const FBXJoint& joint = state.getFBXJoint(); int parentIndex = joint.parentIndex; if (parentIndex == -1) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; state.initTransform(parentTransform); } else { const JointState& parentState = _jointStates.at(parentIndex); state.initTransform(parentState.getTransform()); } } } void Model::init() { if (!_program.isLinked()) { _program.addShaderFromSourceCode(QGLShader::Vertex, model_vert); _program.addShaderFromSourceCode(QGLShader::Fragment, model_frag); initProgram(_program, _locations); _normalMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_normal_map_vert); _normalMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_normal_map_frag); initProgram(_normalMapProgram, _normalMapLocations); _specularMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_vert); _specularMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_specular_map_frag); initProgram(_specularMapProgram, _specularMapLocations); _normalSpecularMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_normal_map_vert); _normalSpecularMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_normal_specular_map_frag); initProgram(_normalSpecularMapProgram, _normalSpecularMapLocations); _translucentProgram.addShaderFromSourceCode(QGLShader::Vertex, model_vert); _translucentProgram.addShaderFromSourceCode(QGLShader::Fragment, model_translucent_frag); initProgram(_translucentProgram, _translucentLocations); // Lightmap _lightmapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_lightmap_vert); _lightmapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_lightmap_frag); initProgram(_lightmapProgram, _lightmapLocations); _lightmapNormalMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_lightmap_normal_map_vert); _lightmapNormalMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_lightmap_normal_map_frag); initProgram(_lightmapNormalMapProgram, _lightmapNormalMapLocations); _lightmapSpecularMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_lightmap_vert); _lightmapSpecularMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_lightmap_specular_map_frag); initProgram(_lightmapSpecularMapProgram, _lightmapSpecularMapLocations); _lightmapNormalSpecularMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_lightmap_normal_map_vert); _lightmapNormalSpecularMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_lightmap_normal_specular_map_frag); initProgram(_lightmapNormalSpecularMapProgram, _lightmapNormalSpecularMapLocations); // end lightmap _shadowProgram.addShaderFromSourceCode(QGLShader::Vertex, model_shadow_vert); _shadowProgram.addShaderFromSourceCode(QGLShader::Fragment, model_shadow_frag); _skinProgram.addShaderFromSourceCode(QGLShader::Vertex, skin_model_vert); _skinProgram.addShaderFromSourceCode(QGLShader::Fragment, model_frag); initSkinProgram(_skinProgram, _skinLocations); _skinNormalMapProgram.addShaderFromSourceCode(QGLShader::Vertex, skin_model_normal_map_vert); _skinNormalMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_normal_map_frag); initSkinProgram(_skinNormalMapProgram, _skinNormalMapLocations); _skinSpecularMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_vert); _skinSpecularMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_specular_map_frag); initSkinProgram(_skinSpecularMapProgram, _skinSpecularMapLocations); _skinNormalSpecularMapProgram.addShaderFromSourceCode(QGLShader::Vertex, skin_model_normal_map_vert); _skinNormalSpecularMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_normal_specular_map_frag); initSkinProgram(_skinNormalSpecularMapProgram, _skinNormalSpecularMapLocations); _skinShadowProgram.addShaderFromSourceCode(QGLShader::Vertex, skin_model_shadow_vert); _skinShadowProgram.addShaderFromSourceCode(QGLShader::Fragment, model_shadow_frag); initSkinProgram(_skinShadowProgram, _skinShadowLocations); _skinTranslucentProgram.addShaderFromSourceCode(QGLShader::Vertex, skin_model_vert); _skinTranslucentProgram.addShaderFromSourceCode(QGLShader::Fragment, model_translucent_frag); initSkinProgram(_skinTranslucentProgram, _skinTranslucentLocations); } } void Model::reset() { if (_jointStates.isEmpty()) { return; } foreach (Model* attachment, _attachments) { attachment->reset(); } const FBXGeometry& geometry = _geometry->getFBXGeometry(); for (int i = 0; i < _jointStates.size(); i++) { _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f); } _meshGroupsKnown = false; } bool Model::updateGeometry() { // NOTE: this is a recursive call that walks all attachments, and their attachments bool needFullUpdate = false; for (int i = 0; i < _attachments.size(); i++) { Model* model = _attachments.at(i); if (model->updateGeometry()) { needFullUpdate = true; } } bool needToRebuild = false; if (_nextGeometry) { _nextGeometry = _nextGeometry->getLODOrFallback(_lodDistance, _nextLODHysteresis); _nextGeometry->setLoadPriority(this, -_lodDistance); _nextGeometry->ensureLoading(); if (_nextGeometry->isLoaded()) { applyNextGeometry(); needToRebuild = true; } } if (!_geometry) { // geometry is not ready return false; } QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis); if (_geometry != geometry) { // NOTE: it is theoretically impossible to reach here after passing through the applyNextGeometry() call above. // Which means we don't need to worry about calling deleteGeometry() below immediately after creating new geometry. const FBXGeometry& newGeometry = geometry->getFBXGeometry(); QVector<JointState> newJointStates = createJointStates(newGeometry); if (! _jointStates.isEmpty()) { // copy the existing joint states const FBXGeometry& oldGeometry = _geometry->getFBXGeometry(); for (QHash<QString, int>::const_iterator it = oldGeometry.jointIndices.constBegin(); it != oldGeometry.jointIndices.constEnd(); it++) { int oldIndex = it.value() - 1; int newIndex = newGeometry.getJointIndex(it.key()); if (newIndex != -1) { newJointStates[newIndex].copyState(_jointStates[oldIndex]); } } } deleteGeometry(); _dilatedTextures.clear(); _geometry = geometry; _meshGroupsKnown = false; setJointStates(newJointStates); needToRebuild = true; } else if (_jointStates.isEmpty()) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); if (fbxGeometry.joints.size() > 0) { setJointStates(createJointStates(fbxGeometry)); needToRebuild = true; } } else if (!geometry->isLoaded()) { deleteGeometry(); _dilatedTextures.clear(); } _geometry->setLoadPriority(this, -_lodDistance); _geometry->ensureLoading(); if (needToRebuild) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterMatrices.resize(mesh.clusters.size()); _meshStates.append(state); gpu::BufferPointer buffer(new gpu::Buffer()); if (!mesh.blendshapes.isEmpty()) { buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3)); buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.vertices.constData()); buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), mesh.normals.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.normals.constData()); } _blendedVertexBuffers.push_back(buffer); } foreach (const FBXAttachment& attachment, fbxGeometry.attachments) { Model* model = new Model(this); model->init(); model->setURL(attachment.url); _attachments.append(model); } needFullUpdate = true; } return needFullUpdate; } // virtual void Model::setJointStates(QVector<JointState> states) { _jointStates = states; initJointTransforms(); int numStates = _jointStates.size(); float radius = 0.0f; for (int i = 0; i < numStates; ++i) { float distance = glm::length(_jointStates[i].getPosition()); if (distance > radius) { radius = distance; } _jointStates[i].buildConstraint(); } for (int i = 0; i < _jointStates.size(); i++) { _jointStates[i].slaveVisibleTransform(); } _boundingRadius = radius; } bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, QString& extraInfo, bool pickAgainstTriangles) { bool intersectedSomething = false; // if we aren't active, we can't ray pick yet... if (!isActive()) { return intersectedSomething; } // extents is the entity relative, scaled, centered extents of the entity glm::vec3 position = _translation; glm::mat4 rotation = glm::mat4_cast(_rotation); glm::mat4 translation = glm::translate(position); glm::mat4 modelToWorldMatrix = translation * rotation; glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix); Extents modelExtents = getMeshExtents(); // NOTE: unrotated glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum; glm::vec3 corner = -(dimensions * _registrationPoint); // since we're going to do the ray picking in the model frame of reference AABox modelFrameBox(corner, dimensions);