From 87471df7a193a114ed77bf14b8a1e72c4c86fcff Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Fri, 21 Nov 2014 18:08:34 -0800 Subject: [PATCH] First version of the lighmap working --- interface/resources/shaders/model.vert | 6 +- .../resources/shaders/model_normal_map.vert | 6 +- interface/resources/shaders/skin_model.vert | 4 +- .../shaders/skin_model_normal_map.vert | 4 +- interface/src/gpu/Stream.h | 1 + interface/src/renderer/GeometryCache.cpp | 7 +- interface/src/renderer/Model.cpp | 384 +++++++----------- interface/src/renderer/Model.h | 68 ++-- libraries/fbx/src/FBXReader.cpp | 260 +++++++++++- libraries/fbx/src/FBXReader.h | 8 +- libraries/shared/src/Transform.h | 20 +- 11 files changed, 468 insertions(+), 300 deletions(-) diff --git a/interface/resources/shaders/model.vert b/interface/resources/shaders/model.vert index a304187591..5ae6ea1cb6 100644 --- a/interface/resources/shaders/model.vert +++ b/interface/resources/shaders/model.vert @@ -11,6 +11,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +const int MAX_TEXCOORDS = 2; + +uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; + // the interpolated normal varying vec4 normal; @@ -22,7 +26,7 @@ void main(void) { gl_FrontColor = gl_Color * gl_FrontMaterial.diffuse; // and the texture coordinates - gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.f, 1.f); // use standard pipeline transform gl_Position = ftransform(); diff --git a/interface/resources/shaders/model_normal_map.vert b/interface/resources/shaders/model_normal_map.vert index 9fde196b06..2e98b8c4fc 100644 --- a/interface/resources/shaders/model_normal_map.vert +++ b/interface/resources/shaders/model_normal_map.vert @@ -11,6 +11,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +const int MAX_TEXCOORDS = 2; + +uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; + // the tangent vector attribute vec3 tangent; @@ -29,7 +33,7 @@ void main(void) { gl_FrontColor = gl_Color * gl_FrontMaterial.diffuse; // and the texture coordinates - gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.f, 1.f); // use standard pipeline transform gl_Position = ftransform(); diff --git a/interface/resources/shaders/skin_model.vert b/interface/resources/shaders/skin_model.vert index 072bf3c336..a8c25e885b 100644 --- a/interface/resources/shaders/skin_model.vert +++ b/interface/resources/shaders/skin_model.vert @@ -11,10 +11,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +const int MAX_TEXCOORDS = 2; const int MAX_CLUSTERS = 128; const int INDICES_PER_VERTEX = 4; uniform mat4 clusterMatrices[MAX_CLUSTERS]; +uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; attribute vec4 clusterIndices; attribute vec4 clusterWeights; @@ -38,7 +40,7 @@ void main(void) { gl_FrontColor = gl_FrontMaterial.diffuse; // and the texture coordinates - gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.f, 1.f); gl_Position = gl_ModelViewProjectionMatrix * position; } diff --git a/interface/resources/shaders/skin_model_normal_map.vert b/interface/resources/shaders/skin_model_normal_map.vert index ac6c77f7cb..bbe5da35af 100644 --- a/interface/resources/shaders/skin_model_normal_map.vert +++ b/interface/resources/shaders/skin_model_normal_map.vert @@ -11,10 +11,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +const int MAX_TEXCOORDS = 2; const int MAX_CLUSTERS = 128; const int INDICES_PER_VERTEX = 4; uniform mat4 clusterMatrices[MAX_CLUSTERS]; +uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; // the tangent vector attribute vec3 tangent; @@ -46,7 +48,7 @@ void main(void) { gl_FrontColor = gl_FrontMaterial.diffuse; // and the texture coordinates - gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.f, 1.f); gl_Position = gl_ModelViewProjectionMatrix * interpolatedPosition; } diff --git a/interface/src/gpu/Stream.h b/interface/src/gpu/Stream.h index 84b2edc5f3..d024182531 100644 --- a/interface/src/gpu/Stream.h +++ b/interface/src/gpu/Stream.h @@ -34,6 +34,7 @@ public: TANGENT, SKIN_CLUSTER_INDEX, SKIN_CLUSTER_WEIGHT, + TEXCOORD1, NUM_INPUT_SLOTS, }; diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index b0636d3321..04ff7bdecd 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -964,7 +964,8 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int texCoords1Offset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int clusterIndicesOffset = texCoords1Offset + mesh.texCoords1.size() * sizeof(glm::vec2); int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); networkMesh._vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); @@ -977,6 +978,8 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { networkMesh._vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.colors.constData()); networkMesh._vertexBuffer->setSubData(texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Resource::Byte*) mesh.texCoords.constData()); + networkMesh._vertexBuffer->setSubData(texCoords1Offset, + mesh.texCoords1.size() * sizeof(glm::vec2), (gpu::Resource::Byte*) mesh.texCoords1.constData()); networkMesh._vertexBuffer->setSubData(clusterIndicesOffset, mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Resource::Byte*) mesh.clusterIndices.constData()); networkMesh._vertexBuffer->setSubData(clusterWeightsOffset, @@ -989,6 +992,7 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { if (mesh.tangents.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, tangentsOffset, sizeof(glm::vec3)); if (mesh.colors.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, colorsOffset, sizeof(glm::vec3)); if (mesh.texCoords.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); + if (mesh.texCoords1.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoords1Offset, sizeof(glm::vec2)); if (mesh.clusterIndices.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); if (mesh.clusterWeights.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); @@ -999,6 +1003,7 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { if (mesh.tangents.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); if (mesh.colors.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); if (mesh.texCoords.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + if (mesh.texCoords1.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); if (mesh.clusterIndices.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); if (mesh.clusterWeights.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); } diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index a4c4e51d69..d2a2378689 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -70,10 +70,10 @@ ProgramObject Model::_specularMapProgram; ProgramObject Model::_normalSpecularMapProgram; ProgramObject Model::_translucentProgram; -ProgramObject Model::_emissiveProgram; -ProgramObject Model::_emissiveNormalMapProgram; -ProgramObject Model::_emissiveSpecularMapProgram; -ProgramObject Model::_emissiveNormalSpecularMapProgram; +ProgramObject Model::_lightmapProgram; +ProgramObject Model::_lightmapNormalMapProgram; +ProgramObject Model::_lightmapSpecularMapProgram; +ProgramObject Model::_lightmapNormalSpecularMapProgram; ProgramObject Model::_shadowProgram; @@ -83,11 +83,6 @@ ProgramObject Model::_skinSpecularMapProgram; ProgramObject Model::_skinNormalSpecularMapProgram; ProgramObject Model::_skinTranslucentProgram; -ProgramObject Model::_skinEmissiveProgram; -ProgramObject Model::_skinEmissiveNormalMapProgram; -ProgramObject Model::_skinEmissiveSpecularMapProgram; -ProgramObject Model::_skinEmissiveNormalSpecularMapProgram; - ProgramObject Model::_skinShadowProgram; Model::Locations Model::_locations; @@ -96,10 +91,10 @@ Model::Locations Model::_specularMapLocations; Model::Locations Model::_normalSpecularMapLocations; Model::Locations Model::_translucentLocations; -Model::Locations Model::_emissiveLocations; -Model::Locations Model::_emissiveNormalMapLocations; -Model::Locations Model::_emissiveSpecularMapLocations; -Model::Locations Model::_emissiveNormalSpecularMapLocations; +Model::Locations Model::_lightmapLocations; +Model::Locations Model::_lightmapNormalMapLocations; +Model::Locations Model::_lightmapSpecularMapLocations; +Model::Locations Model::_lightmapNormalSpecularMapLocations; Model::SkinLocations Model::_skinLocations; Model::SkinLocations Model::_skinNormalMapLocations; @@ -108,11 +103,6 @@ Model::SkinLocations Model::_skinNormalSpecularMapLocations; Model::SkinLocations Model::_skinShadowLocations; Model::SkinLocations Model::_skinTranslucentLocations; -Model::SkinLocations Model::_skinEmissiveLocations; -Model::SkinLocations Model::_skinEmissiveNormalMapLocations; -Model::SkinLocations Model::_skinEmissiveSpecularMapLocations; -Model::SkinLocations Model::_skinEmissiveNormalSpecularMapLocations; - void Model::setScale(const glm::vec3& scale) { setScaleInternal(scale); // if anyone sets scale manually, then we are no longer scaled to fit @@ -160,17 +150,40 @@ void Model::initProgram(ProgramObject& program, Model::Locations& locations, int glBindAttribLocation(program.programId(), gpu::Stream::TANGENT, "tangent"); + glBindAttribLocation(program.programId(), gpu::Stream::TEXCOORD1, "texcoord1"); + glLinkProgram(program.programId()); locations.tangent = program.attributeLocation("tangent"); locations.alphaThreshold = program.uniformLocation("alphaThreshold"); + locations.texcoordMatrices = program.uniformLocation("texcoordMatrices"); + + program.setUniformValue("diffuseMap", 0); program.setUniformValue("normalMap", 1); - program.setUniformValue("specularMap", specularTextureUnit); + 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; + } + + if (!program.isLinked()) { + program.release(); + } program.release(); @@ -289,37 +302,37 @@ void Model::init() { initProgram(_translucentProgram, _translucentLocations); - // Emissive - _emissiveProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model.vert"); - _emissiveProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model_emissive.frag"); - _emissiveProgram.link(); + // Lightmap + _lightmapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_lightmap.vert"); + _lightmapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model_lightmap.frag"); + _lightmapProgram.link(); - initProgram(_emissiveProgram, _emissiveLocations); + initProgram(_lightmapProgram, _lightmapLocations); - _emissiveNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, - Application::resourcesPath() + "shaders/model_normal_map.vert"); - _emissiveNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, - Application::resourcesPath() + "shaders/model_emissive_normal_map.frag"); - _emissiveNormalMapProgram.link(); + _lightmapNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/model_lightmap_normal_map.vert"); + _lightmapNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/model_lightmap_normal_map.frag"); + _lightmapNormalMapProgram.link(); - initProgram(_emissiveNormalMapProgram, _emissiveNormalMapLocations); + initProgram(_lightmapNormalMapProgram, _lightmapNormalMapLocations); - _emissiveSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, - Application::resourcesPath() + "shaders/model.vert"); - _emissiveSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment, - Application::resourcesPath() + "shaders/model_emissive_specular_map.frag"); - _emissiveSpecularMapProgram.link(); + _lightmapSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/model_lightmap.vert"); + _lightmapSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/model_lightmap_specular_map.frag"); + _lightmapSpecularMapProgram.link(); - initProgram(_emissiveSpecularMapProgram, _emissiveSpecularMapLocations); + initProgram(_lightmapSpecularMapProgram, _lightmapSpecularMapLocations); - _emissiveNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, - Application::resourcesPath() + "shaders/model_normal_map.vert"); - _emissiveNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment, - Application::resourcesPath() + "shaders/model_emissive_normal_specular_map.frag"); - _emissiveNormalSpecularMapProgram.link(); + _lightmapNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/model_lightmap_normal_map.vert"); + _lightmapNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/model_lightmap_normal_specular_map.frag"); + _lightmapNormalSpecularMapProgram.link(); - initProgram(_emissiveNormalSpecularMapProgram, _emissiveNormalSpecularMapLocations, 2); - // end emissive + initProgram(_lightmapNormalSpecularMapProgram, _lightmapNormalSpecularMapLocations, 2); + // end lightmap _shadowProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_shadow.vert"); @@ -374,36 +387,6 @@ void Model::init() { initSkinProgram(_skinTranslucentProgram, _skinTranslucentLocations); - _skinEmissiveProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/skin_model.vert"); - _skinEmissiveProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model_emissive.frag"); - _skinEmissiveProgram.link(); - - initSkinProgram(_skinEmissiveProgram, _skinEmissiveLocations); - - _skinEmissiveNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, - Application::resourcesPath() + "shaders/skin_model_normal_map.vert"); - _skinEmissiveNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, - Application::resourcesPath() + "shaders/model_emissive_normal_map.frag"); - _skinEmissiveNormalMapProgram.link(); - - initSkinProgram(_skinEmissiveNormalMapProgram, _skinEmissiveNormalMapLocations); - - _skinEmissiveSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, - Application::resourcesPath() + "shaders/skin_model.vert"); - _skinEmissiveSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment, - Application::resourcesPath() + "shaders/model_emissive_specular_map.frag"); - _skinEmissiveSpecularMapProgram.link(); - - initSkinProgram(_skinEmissiveSpecularMapProgram, _skinEmissiveSpecularMapLocations); - - _skinEmissiveNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, - Application::resourcesPath() + "shaders/skin_model_normal_map.vert"); - _skinEmissiveNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment, - Application::resourcesPath() + "shaders/model_emissive_normal_specular_map.frag"); - _skinEmissiveNormalSpecularMapProgram.link(); - - initSkinProgram(_skinEmissiveNormalSpecularMapProgram, _skinEmissiveNormalSpecularMapLocations, 2); - } } @@ -716,13 +699,9 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, true, args); opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, false, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, true, args); opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, false, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, true, args); opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, false, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, true, args); opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, false, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, true, args); // render translucent meshes afterwards //Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(false, true, true); @@ -1666,13 +1645,9 @@ void Model::endScene(RenderMode mode, RenderArgs* args) { opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, true, args); */ opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, true, args); opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, true, args); opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, true, args); opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, true, args); // render translucent meshes afterwards //Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(false, true, true); @@ -1793,15 +1768,10 @@ void Model::segregateMeshGroups() { _meshesOpaqueTangentsSpecularSkinned.clear(); _meshesOpaqueSpecularSkinned.clear(); - _meshesOpaqueEmissiveTangents.clear(); - _meshesOpaqueEmissive.clear(); - _meshesOpaqueEmissiveTangentsSpecular.clear(); - _meshesOpaqueEmissiveSpecular.clear(); - - _meshesOpaqueEmissiveTangentsSkinned.clear(); - _meshesOpaqueEmissiveSkinned.clear(); - _meshesOpaqueEmissiveTangentsSpecularSkinned.clear(); - _meshesOpaqueEmissiveSpecularSkinned.clear(); + _meshesOpaqueLightmapTangents.clear(); + _meshesOpaqueLightmap.clear(); + _meshesOpaqueLightmapTangentsSpecular.clear(); + _meshesOpaqueLightmapSpecular.clear(); _unsortedMeshesTranslucentTangents.clear(); _unsortedMeshesTranslucent.clear(); @@ -1823,15 +1793,10 @@ void Model::segregateMeshGroups() { _unsortedMeshesOpaqueTangentsSpecularSkinned.clear(); _unsortedMeshesOpaqueSpecularSkinned.clear(); - _unsortedMeshesOpaqueEmissiveTangents.clear(); - _unsortedMeshesOpaqueEmissive.clear(); - _unsortedMeshesOpaqueEmissiveTangentsSpecular.clear(); - _unsortedMeshesOpaqueEmissiveSpecular.clear(); - - _unsortedMeshesOpaqueEmissiveTangentsSkinned.clear(); - _unsortedMeshesOpaqueEmissiveSkinned.clear(); - _unsortedMeshesOpaqueEmissiveTangentsSpecularSkinned.clear(); - _unsortedMeshesOpaqueEmissiveSpecularSkinned.clear(); + _unsortedMeshesOpaqueLightmapTangents.clear(); + _unsortedMeshesOpaqueLightmap.clear(); + _unsortedMeshesOpaqueLightmapTangentsSpecular.clear(); + _unsortedMeshesOpaqueLightmapSpecular.clear(); const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& networkMeshes = _geometry->getMeshes(); @@ -1846,7 +1811,7 @@ void Model::segregateMeshGroups() { bool translucentMesh = networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size(); bool hasTangents = !mesh.tangents.isEmpty(); bool hasSpecular = mesh.hasSpecularTexture(); - bool hasEmissive = mesh.hasEmissiveTexture(); + bool haslightmap = mesh.hasEmissiveTexture(); bool isSkinned = state.clusterMatrices.size() > 1; QString materialID; @@ -1865,7 +1830,7 @@ void Model::segregateMeshGroups() { qDebug() << "materialID:" << materialID << "parts:" << mesh.parts.size(); } - if ( !hasEmissive) { + if ( !haslightmap) { if (translucentMesh && !hasTangents && !hasSpecular && !isSkinned) { _unsortedMeshesTranslucent.insertMulti(materialID, i); @@ -1935,35 +1900,20 @@ void Model::segregateMeshGroups() { } else { if (!translucentMesh && !hasTangents && !hasSpecular && !isSkinned) { - _unsortedMeshesOpaqueEmissive.insertMulti(materialID, i); + _unsortedMeshesOpaqueLightmap.insertMulti(materialID, i); } else if (!translucentMesh && hasTangents && !hasSpecular && !isSkinned) { - _unsortedMeshesOpaqueEmissiveTangents.insertMulti(materialID, i); + _unsortedMeshesOpaqueLightmapTangents.insertMulti(materialID, i); } else if (!translucentMesh && hasTangents && hasSpecular && !isSkinned) { - _unsortedMeshesOpaqueEmissiveTangentsSpecular.insertMulti(materialID, i); + _unsortedMeshesOpaqueLightmapTangentsSpecular.insertMulti(materialID, i); } else if (!translucentMesh && !hasTangents && hasSpecular && !isSkinned) { - _unsortedMeshesOpaqueEmissiveSpecular.insertMulti(materialID, i); + _unsortedMeshesOpaqueLightmapSpecular.insertMulti(materialID, i); - } else if (!translucentMesh && hasTangents && !hasSpecular && isSkinned) { - - _unsortedMeshesOpaqueEmissiveTangentsSkinned.insertMulti(materialID, i); - - } else if (!translucentMesh && !hasTangents && !hasSpecular && isSkinned) { - - _unsortedMeshesOpaqueEmissiveSkinned.insertMulti(materialID, i); - - } else if (!translucentMesh && hasTangents && hasSpecular && isSkinned) { - - _unsortedMeshesOpaqueEmissiveTangentsSpecularSkinned.insertMulti(materialID, i); - - } else if (!translucentMesh && !hasTangents && hasSpecular && isSkinned) { - - _unsortedMeshesOpaqueEmissiveSpecularSkinned.insertMulti(materialID, i); } else { qDebug() << "unexpected!!! this mesh didn't fall into any or our groups???"; } @@ -2034,36 +1984,20 @@ void Model::segregateMeshGroups() { _meshesOpaqueSpecularSkinned.append(i); } - foreach(int i, _unsortedMeshesOpaqueEmissive) { - _meshesOpaqueEmissive.append(i); + foreach(int i, _unsortedMeshesOpaqueLightmap) { + _meshesOpaqueLightmap.append(i); } - foreach(int i, _unsortedMeshesOpaqueEmissiveTangents) { - _meshesOpaqueEmissiveTangents.append(i); + foreach(int i, _unsortedMeshesOpaqueLightmapTangents) { + _meshesOpaqueLightmapTangents.append(i); } - foreach(int i, _unsortedMeshesOpaqueEmissiveTangentsSpecular) { - _meshesOpaqueEmissiveTangentsSpecular.append(i); + foreach(int i, _unsortedMeshesOpaqueLightmapTangentsSpecular) { + _meshesOpaqueLightmapTangentsSpecular.append(i); } - foreach(int i, _unsortedMeshesOpaqueEmissiveSpecular) { - _meshesOpaqueEmissiveSpecular.append(i); - } - - foreach(int i, _unsortedMeshesOpaqueEmissiveSkinned) { - _meshesOpaqueEmissiveSkinned.append(i); - } - - foreach(int i, _unsortedMeshesOpaqueEmissiveTangentsSkinned) { - _meshesOpaqueEmissiveTangentsSkinned.append(i); - } - - foreach(int i, _unsortedMeshesOpaqueEmissiveTangentsSpecularSkinned) { - _meshesOpaqueEmissiveTangentsSpecularSkinned.append(i); - } - - foreach(int i, _unsortedMeshesOpaqueEmissiveSpecularSkinned) { - _meshesOpaqueEmissiveSpecularSkinned.append(i); + foreach(int i, _unsortedMeshesOpaqueLightmapSpecular) { + _meshesOpaqueLightmapSpecular.append(i); } _unsortedMeshesTranslucentTangents.clear(); @@ -2086,20 +2020,15 @@ void Model::segregateMeshGroups() { _unsortedMeshesOpaqueTangentsSpecularSkinned.clear(); _unsortedMeshesOpaqueSpecularSkinned.clear(); - _unsortedMeshesOpaqueEmissiveTangents.clear(); - _unsortedMeshesOpaqueEmissive.clear(); - _unsortedMeshesOpaqueEmissiveTangentsSpecular.clear(); - _unsortedMeshesOpaqueEmissiveSpecular.clear(); - - _unsortedMeshesOpaqueEmissiveTangentsSkinned.clear(); - _unsortedMeshesOpaqueEmissiveSkinned.clear(); - _unsortedMeshesOpaqueEmissiveTangentsSpecularSkinned.clear(); - _unsortedMeshesOpaqueEmissiveSpecularSkinned.clear(); + _unsortedMeshesOpaqueLightmapTangents.clear(); + _unsortedMeshesOpaqueLightmap.clear(); + _unsortedMeshesOpaqueLightmapTangentsSpecular.clear(); + _unsortedMeshesOpaqueLightmapSpecular.clear(); _meshGroupsKnown = true; } -QVector* Model::pickMeshList(bool translucent, float alphaThreshold, bool hasEmissive, bool hasTangents, bool hasSpecular, bool isSkinned) { +QVector* Model::pickMeshList(bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned) { PROFILE_RANGE(__FUNCTION__); // depending on which parameters we were called with, pick the correct mesh group to render @@ -2121,39 +2050,31 @@ QVector* Model::pickMeshList(bool translucent, float alphaThreshold, bool h } else if (translucent && !hasTangents && hasSpecular && isSkinned) { whichList = &_meshesTranslucentSpecularSkinned; - } else if (!translucent && !hasEmissive && !hasTangents && !hasSpecular && !isSkinned) { + } else if (!translucent && !hasLightmap && !hasTangents && !hasSpecular && !isSkinned) { whichList = &_meshesOpaque; - } else if (!translucent && !hasEmissive && hasTangents && !hasSpecular && !isSkinned) { + } else if (!translucent && !hasLightmap && hasTangents && !hasSpecular && !isSkinned) { whichList = &_meshesOpaqueTangents; - } else if (!translucent && !hasEmissive && hasTangents && hasSpecular && !isSkinned) { + } else if (!translucent && !hasLightmap && hasTangents && hasSpecular && !isSkinned) { whichList = &_meshesOpaqueTangentsSpecular; - } else if (!translucent && !hasEmissive && !hasTangents && hasSpecular && !isSkinned) { + } else if (!translucent && !hasLightmap && !hasTangents && hasSpecular && !isSkinned) { whichList = &_meshesOpaqueSpecular; - } else if (!translucent && !hasEmissive && hasTangents && !hasSpecular && isSkinned) { + } else if (!translucent && !hasLightmap && hasTangents && !hasSpecular && isSkinned) { whichList = &_meshesOpaqueTangentsSkinned; - } else if (!translucent && !hasEmissive && !hasTangents && !hasSpecular && isSkinned) { + } else if (!translucent && !hasLightmap && !hasTangents && !hasSpecular && isSkinned) { whichList = &_meshesOpaqueSkinned; - } else if (!translucent && !hasEmissive && hasTangents && hasSpecular && isSkinned) { + } else if (!translucent && !hasLightmap && hasTangents && hasSpecular && isSkinned) { whichList = &_meshesOpaqueTangentsSpecularSkinned; - } else if (!translucent && !hasEmissive && !hasTangents && hasSpecular && isSkinned) { + } else if (!translucent && !hasLightmap && !hasTangents && hasSpecular && isSkinned) { whichList = &_meshesOpaqueSpecularSkinned; - } else if (!translucent && hasEmissive && !hasTangents && !hasSpecular && !isSkinned) { - whichList = &_meshesOpaqueEmissive; - } else if (!translucent && hasEmissive && hasTangents && !hasSpecular && !isSkinned) { - whichList = &_meshesOpaqueEmissiveTangents; - } else if (!translucent && hasEmissive && hasTangents && hasSpecular && !isSkinned) { - whichList = &_meshesOpaqueEmissiveTangentsSpecular; - } else if (!translucent && hasEmissive && !hasTangents && hasSpecular && !isSkinned) { - whichList = &_meshesOpaqueEmissiveSpecular; - } else if (!translucent && hasEmissive && hasTangents && !hasSpecular && isSkinned) { - whichList = &_meshesOpaqueEmissiveTangentsSkinned; - } else if (!translucent && hasEmissive && !hasTangents && !hasSpecular && isSkinned) { - whichList = &_meshesOpaqueEmissiveSkinned; - } else if (!translucent && hasEmissive && hasTangents && hasSpecular && isSkinned) { - whichList = &_meshesOpaqueEmissiveTangentsSpecularSkinned; - } else if (!translucent && hasEmissive && !hasTangents && hasSpecular && isSkinned) { - whichList = &_meshesOpaqueEmissiveSpecularSkinned; + } else if (!translucent && hasLightmap && !hasTangents && !hasSpecular && !isSkinned) { + whichList = &_meshesOpaqueLightmap; + } else if (!translucent && hasLightmap && hasTangents && !hasSpecular && !isSkinned) { + whichList = &_meshesOpaqueLightmapTangents; + } else if (!translucent && hasLightmap && hasTangents && hasSpecular && !isSkinned) { + whichList = &_meshesOpaqueLightmapTangentsSpecular; + } else if (!translucent && hasLightmap && !hasTangents && hasSpecular && !isSkinned) { + whichList = &_meshesOpaqueLightmapSpecular; } else { qDebug() << "unexpected!!! this mesh didn't fall into any or our groups???"; @@ -2162,15 +2083,13 @@ QVector* Model::pickMeshList(bool translucent, float alphaThreshold, bool h } void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - bool hasEmissive, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args, - SkinLocations*& skinLocations, GLenum& specularTextureUnit, GLenum& emissiveTextureUnit) { + bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args, + Locations*& locations, SkinLocations*& skinLocations) { ProgramObject* program = &_program; - Locations* locations = &_locations; + locations = &_locations; ProgramObject* skinProgram = &_skinProgram; skinLocations = &_skinLocations; - specularTextureUnit = 0; - emissiveTextureUnit = 0; if (mode == SHADOW_RENDER_MODE) { program = &_shadowProgram; skinProgram = &_skinShadowProgram; @@ -2181,35 +2100,29 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f skinProgram = &_skinTranslucentProgram; skinLocations = &_skinTranslucentLocations; - } else if (hasEmissive) { + } else if (hasLightmap) { if (hasTangents) { if (hasSpecular) { - program = &_emissiveNormalSpecularMapProgram; - locations = &_emissiveNormalSpecularMapLocations; - skinProgram = &_skinEmissiveNormalSpecularMapProgram; - skinLocations = &_skinEmissiveNormalSpecularMapLocations; - specularTextureUnit = GL_TEXTURE2; - emissiveTextureUnit = GL_TEXTURE3; + program = &_lightmapNormalSpecularMapProgram; + locations = &_lightmapNormalSpecularMapLocations; + skinProgram = NULL; + skinLocations = NULL; } else { - program = &_emissiveNormalMapProgram; - locations = &_emissiveNormalMapLocations; - skinProgram = &_skinEmissiveNormalMapProgram; - skinLocations = &_skinEmissiveNormalMapLocations; - emissiveTextureUnit = GL_TEXTURE3; + program = &_lightmapNormalMapProgram; + locations = &_lightmapNormalMapLocations; + skinProgram = NULL; + skinLocations = NULL; } } else if (hasSpecular) { - program = &_emissiveSpecularMapProgram; - locations = &_emissiveSpecularMapLocations; - skinProgram = &_skinEmissiveSpecularMapProgram; - skinLocations = &_skinEmissiveSpecularMapLocations; - specularTextureUnit = GL_TEXTURE1; - emissiveTextureUnit = GL_TEXTURE3; + program = &_lightmapSpecularMapProgram; + locations = &_lightmapSpecularMapLocations; + skinProgram = NULL; + skinLocations = NULL; } else { - program = &_emissiveProgram; - locations = &_emissiveLocations; - skinProgram = &_skinEmissiveProgram; - skinLocations = &_skinEmissiveLocations; - emissiveTextureUnit = GL_TEXTURE3; + program = &_lightmapProgram; + locations = &_lightmapLocations; + skinProgram = NULL; + skinLocations = NULL; } } else { if (hasTangents) { @@ -2218,7 +2131,6 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f locations = &_normalSpecularMapLocations; skinProgram = &_skinNormalSpecularMapProgram; skinLocations = &_skinNormalSpecularMapLocations; - specularTextureUnit = GL_TEXTURE2; } else { program = &_normalMapProgram; locations = &_normalMapLocations; @@ -2230,7 +2142,6 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f locations = &_specularMapLocations; skinProgram = &_skinSpecularMapProgram; skinLocations = &_skinSpecularMapLocations; - specularTextureUnit = GL_TEXTURE1; } } @@ -2240,6 +2151,7 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f if (isSkinned) { activeProgram = skinProgram; activeLocations = skinLocations; + locations = skinLocations; } // This code replace the "bind()" on the QGLProgram if (!activeProgram->isLinked()) { @@ -2251,27 +2163,26 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f } int Model::renderMeshesForModelsInScene(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - bool hasEmissive, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) { + bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) { PROFILE_RANGE(__FUNCTION__); int meshPartsRendered = 0; bool pickProgramsNeeded = true; + Locations* locations; SkinLocations* skinLocations; - GLenum specularTextureUnit; - GLenum emissiveTextureUnit; foreach(Model* model, _modelsInScene) { - QVector* whichList = model->pickMeshList(translucent, alphaThreshold, hasEmissive, hasTangents, hasSpecular, isSkinned); + QVector* whichList = model->pickMeshList(translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned); if (whichList) { QVector& list = *whichList; if (list.size() > 0) { if (pickProgramsNeeded) { - pickPrograms(batch, mode, translucent, alphaThreshold, hasEmissive, hasTangents, hasSpecular, isSkinned, args, skinLocations, specularTextureUnit, emissiveTextureUnit); + pickPrograms(batch, mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, args, locations, skinLocations); pickProgramsNeeded = false; } model->setupBatchTransform(batch); - meshPartsRendered += model->renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, args, skinLocations, specularTextureUnit, emissiveTextureUnit); + meshPartsRendered += model->renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, args, locations, skinLocations); GLBATCH(glPopMatrix)(); } } @@ -2284,12 +2195,12 @@ int Model::renderMeshesForModelsInScene(gpu::Batch& batch, RenderMode mode, bool } int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - bool hasEmissive, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) { + bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) { PROFILE_RANGE(__FUNCTION__); int meshPartsRendered = 0; - QVector* whichList = pickMeshList(translucent, alphaThreshold, hasEmissive, hasTangents, hasSpecular, isSkinned); + QVector* whichList = pickMeshList(translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned); if (!whichList) { qDebug() << "unexpected!!! we don't know which list of meshes to render..."; @@ -2302,11 +2213,10 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl return 0; } + Locations* locations; SkinLocations* skinLocations; - GLenum specularTextureUnit; - GLenum emissiveTextureUnit; - pickPrograms(batch, mode, translucent, alphaThreshold, hasEmissive, hasTangents, hasSpecular, isSkinned, args, skinLocations, specularTextureUnit, emissiveTextureUnit); - meshPartsRendered = renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, args, skinLocations, specularTextureUnit, emissiveTextureUnit); + pickPrograms(batch, mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, args, locations, skinLocations); + meshPartsRendered = renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, args, locations, skinLocations); GLBATCH(glUseProgram)(0); return meshPartsRendered; @@ -2314,7 +2224,7 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, RenderArgs* args, - SkinLocations* skinLocations, GLenum specularTextureUnit, GLenum emissiveTextureUnit) { + Locations* locations, SkinLocations* skinLocations) { PROFILE_RANGE(__FUNCTION__); bool dontCullOutOfViewMeshParts = Menu::getInstance()->isOptionChecked(MenuOption::DontCullOutOfViewMeshParts); bool cullTooSmallMeshParts = !Menu::getInstance()->isOptionChecked(MenuOption::DontCullTooSmallMeshParts); @@ -2431,7 +2341,7 @@ int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMod GLBATCH(glMaterialfv)(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse); GLBATCH(glMaterialfv)(GL_FRONT, GL_SPECULAR, (const float*)&specular); GLBATCH(glMaterialf)(GL_FRONT, GL_SHININESS, (part.shininess > 128.0f ? 128.0f: part.shininess)); - + Texture* diffuseMap = networkPart.diffuseTexture.data(); if (mesh.isEye && diffuseMap) { diffuseMap = (_dilatedTextures[i][j] = @@ -2439,7 +2349,18 @@ int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMod } GLBATCH(glBindTexture)(GL_TEXTURE_2D, !diffuseMap ? Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID()); - + + if (locations->texcoordMatrices >= 0) { + glm::mat4 texcoordTransform[2]; + if (!part.diffuseTexture.transform.isIdentity()) { + part.diffuseTexture.transform.getMatrix(texcoordTransform[0]); + } + if (!part.emissiveTexture.transform.isIdentity()) { + part.emissiveTexture.transform.getMatrix(texcoordTransform[1]); + } + GLBATCH(glUniformMatrix4fv)(locations->texcoordMatrices, 2, false, (const float*) &texcoordTransform); + } + if (!mesh.tangents.isEmpty()) { GLBATCH(glActiveTexture)(GL_TEXTURE1); Texture* normalMap = networkPart.normalTexture.data(); @@ -2448,19 +2369,20 @@ int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMod GLBATCH(glActiveTexture)(GL_TEXTURE0); } - if (specularTextureUnit) { - GLBATCH(glActiveTexture)(specularTextureUnit); + if (locations->specularTextureUnit >= 0) { + GLBATCH(glActiveTexture)(GL_TEXTURE0 + locations->specularTextureUnit); Texture* specularMap = networkPart.specularTexture.data(); GLBATCH(glBindTexture)(GL_TEXTURE_2D, !specularMap ? Application::getInstance()->getTextureCache()->getWhiteTextureID() : specularMap->getID()); GLBATCH(glActiveTexture)(GL_TEXTURE0); } - if (emissiveTextureUnit) { - GLBATCH(glActiveTexture)(emissiveTextureUnit); + if (locations->emissiveTextureUnit >= 0) { + GLBATCH(glActiveTexture)(GL_TEXTURE0 + locations->emissiveTextureUnit); Texture* emissiveMap = networkPart.emissiveTexture.data(); GLBATCH(glBindTexture)(GL_TEXTURE_2D, !emissiveMap ? Application::getInstance()->getTextureCache()->getWhiteTextureID() : emissiveMap->getID()); + // GLBATCH(glBindTexture)(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getWhiteTextureID()); GLBATCH(glActiveTexture)(GL_TEXTURE0); } @@ -2498,14 +2420,14 @@ int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMod GLBATCH(glActiveTexture)(GL_TEXTURE0); } - if (specularTextureUnit) { - GLBATCH(glActiveTexture)(specularTextureUnit); + if (locations->specularTextureUnit >= 0) { + GLBATCH(glActiveTexture)(GL_TEXTURE0 + locations->specularTextureUnit); GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); GLBATCH(glActiveTexture)(GL_TEXTURE0); } - if (emissiveTextureUnit) { - GLBATCH(glActiveTexture)(emissiveTextureUnit); + if (locations->emissiveTextureUnit >= 0) { + GLBATCH(glActiveTexture)(GL_TEXTURE0 + locations->emissiveTextureUnit); GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); GLBATCH(glActiveTexture)(GL_TEXTURE0); } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index c1ae190a1e..7a3457423e 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -304,10 +304,10 @@ private: static ProgramObject _normalSpecularMapProgram; static ProgramObject _translucentProgram; - static ProgramObject _emissiveProgram; - static ProgramObject _emissiveNormalMapProgram; - static ProgramObject _emissiveSpecularMapProgram; - static ProgramObject _emissiveNormalSpecularMapProgram; + static ProgramObject _lightmapProgram; + static ProgramObject _lightmapNormalMapProgram; + static ProgramObject _lightmapSpecularMapProgram; + static ProgramObject _lightmapNormalSpecularMapProgram; static ProgramObject _shadowProgram; @@ -317,11 +317,6 @@ private: static ProgramObject _skinNormalSpecularMapProgram; static ProgramObject _skinTranslucentProgram; - static ProgramObject _skinEmissiveProgram; - static ProgramObject _skinEmissiveNormalMapProgram; - static ProgramObject _skinEmissiveSpecularMapProgram; - static ProgramObject _skinEmissiveNormalSpecularMapProgram; - static ProgramObject _skinShadowProgram; static int _normalMapTangentLocation; @@ -331,6 +326,9 @@ private: public: int tangent; int alphaThreshold; + int texcoordMatrices; + int specularTextureUnit; + int emissiveTextureUnit; }; static Locations _locations; @@ -339,10 +337,10 @@ private: static Locations _normalSpecularMapLocations; static Locations _translucentLocations; - static Locations _emissiveLocations; - static Locations _emissiveNormalMapLocations; - static Locations _emissiveSpecularMapLocations; - static Locations _emissiveNormalSpecularMapLocations; + static Locations _lightmapLocations; + static Locations _lightmapNormalMapLocations; + static Locations _lightmapSpecularMapLocations; + static Locations _lightmapNormalSpecularMapLocations; static void initProgram(ProgramObject& program, Locations& locations, int specularTextureUnit = 1); @@ -350,7 +348,7 @@ private: public: int clusterMatrices; int clusterIndices; - int clusterWeights; + int clusterWeights; }; static SkinLocations _skinLocations; @@ -360,11 +358,6 @@ private: static SkinLocations _skinShadowLocations; static SkinLocations _skinTranslucentLocations; - static SkinLocations _skinEmissiveLocations; - static SkinLocations _skinEmissiveNormalMapLocations; - static SkinLocations _skinEmissiveSpecularMapLocations; - static SkinLocations _skinEmissiveNormalSpecularMapLocations; - static void initSkinProgram(ProgramObject& program, SkinLocations& locations, int specularTextureUnit = 1); QVector _calculatedMeshBoxes; @@ -396,15 +389,10 @@ private: QMap _unsortedMeshesOpaqueTangentsSpecularSkinned; QMap _unsortedMeshesOpaqueSpecularSkinned; - QMap _unsortedMeshesOpaqueEmissive; - QMap _unsortedMeshesOpaqueEmissiveTangents; - QMap _unsortedMeshesOpaqueEmissiveTangentsSpecular; - QMap _unsortedMeshesOpaqueEmissiveSpecular; - - QMap _unsortedMeshesOpaqueEmissiveSkinned; - QMap _unsortedMeshesOpaqueEmissiveTangentsSkinned; - QMap _unsortedMeshesOpaqueEmissiveTangentsSpecularSkinned; - QMap _unsortedMeshesOpaqueEmissiveSpecularSkinned; + QMap _unsortedMeshesOpaqueLightmap; + QMap _unsortedMeshesOpaqueLightmapTangents; + QMap _unsortedMeshesOpaqueLightmapTangentsSpecular; + QMap _unsortedMeshesOpaqueLightmapSpecular; QVector _meshesTranslucent; QVector _meshesTranslucentTangents; @@ -426,15 +414,11 @@ private: QVector _meshesOpaqueTangentsSpecularSkinned; QVector _meshesOpaqueSpecularSkinned; - QVector _meshesOpaqueEmissive; - QVector _meshesOpaqueEmissiveTangents; - QVector _meshesOpaqueEmissiveTangentsSpecular; - QVector _meshesOpaqueEmissiveSpecular; + QVector _meshesOpaqueLightmap; + QVector _meshesOpaqueLightmapTangents; + QVector _meshesOpaqueLightmapTangentsSpecular; + QVector _meshesOpaqueLightmapSpecular; - QVector _meshesOpaqueEmissiveSkinned; - QVector _meshesOpaqueEmissiveTangentsSkinned; - QVector _meshesOpaqueEmissiveTangentsSpecularSkinned; - QVector _meshesOpaqueEmissiveSpecularSkinned; // Scene rendering support static QVector _modelsInScene; @@ -447,19 +431,19 @@ private: void renderSetup(RenderArgs* args); bool renderCore(float alpha, RenderMode mode, RenderArgs* args); int renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - bool hasEmissive, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args = NULL); + bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args = NULL); void setupBatchTransform(gpu::Batch& batch); - QVector* pickMeshList(bool translucent, float alphaThreshold, bool hasEmissive, bool hasTangents, bool hasSpecular, bool isSkinned); + QVector* pickMeshList(bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned); int renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - RenderArgs* args, SkinLocations* skinLocations, GLenum specularTextureUnit, GLenum emissiveTextureUnit); + RenderArgs* args, Locations* locations, SkinLocations* skinLocations); static void pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - bool hasEmissive, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args, - SkinLocations*& skinLocations, GLenum& specularTextureUnit, GLenum& emissiveTextureUnit); + bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args, + Locations*& locations, SkinLocations*& skinLocations); static int renderMeshesForModelsInScene(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - bool hasEmissive, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args); + bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args); }; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 2a97c50fea..130f1db550 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -32,6 +32,48 @@ using namespace std; +struct TextureParam { + glm::vec2 UVTranslation; + glm::vec2 UVScaling; + glm::vec4 cropping; + std::string UVSet; + + glm::vec3 translation; + glm::vec3 rotation; + glm::vec3 scaling; + uint8_t alphaSource; + uint8_t currentTextureBlendMode; + bool useMaterial; + + template + bool assign(T& ref, const T& v) { + if (ref == v) { + return false; + } else { + ref = v; + isDefault = false; + return true; + } + } + + bool isDefault; + + TextureParam() : + UVTranslation(0.0f), + UVScaling(1.0f), + cropping(0.0f), + UVSet("map1"), + translation(0.0f), + rotation(0.0f), + scaling(1.0f), + alphaSource(0), + currentTextureBlendMode(0), + useMaterial(true), + isDefault(true) + {} +}; + + bool FBXMesh::hasSpecularTexture() const { foreach (const FBXMeshPart& part, parts) { if (!part.specularTexture.filename.isEmpty()) { @@ -718,6 +760,7 @@ class Vertex { public: int originalIndex; glm::vec2 texCoord; + glm::vec2 texCoord1; }; uint qHash(const Vertex& vertex, uint seed = 0) { @@ -725,7 +768,7 @@ uint qHash(const Vertex& vertex, uint seed = 0) { } bool operator==(const Vertex& v1, const Vertex& v2) { - return v1.originalIndex == v2.originalIndex && v1.texCoord == v2.texCoord; + return v1.originalIndex == v2.originalIndex && v1.texCoord == v2.texCoord && v1.texCoord1 == v2.texCoord1; } class ExtractedMesh { @@ -734,6 +777,16 @@ public: QMultiHash newIndices; QVector > blendshapeIndexMaps; QVector > partMaterialTextures; + QHash texcoordSetMap; + std::map texcoordSetMap2; +}; + +class AttributeData { +public: + QVector texCoords; + QVector texCoordIndices; + std::string name; + int index; }; class MeshData { @@ -748,6 +801,8 @@ public: QVector texCoordIndices; QHash indices; + + std::vector attributes; }; void appendIndex(MeshData& data, QVector& indices, int index) { @@ -789,6 +844,20 @@ void appendIndex(MeshData& data, QVector& indices, int index) { vertex.texCoord = data.texCoords.at(texCoordIndex); } } + + bool hasMoreTexcoords = (data.attributes.size() > 1); + if (hasMoreTexcoords) { + if (data.attributes[1].texCoordIndices.empty()) { + if (index < data.attributes[1].texCoords.size()) { + vertex.texCoord1 = data.attributes[1].texCoords.at(index); + } + } else if (index < data.attributes[1].texCoordIndices.size()) { + int texCoordIndex = data.attributes[1].texCoordIndices.at(index); + if (texCoordIndex >= 0 && texCoordIndex < data.attributes[1].texCoords.size()) { + vertex.texCoord1 = data.attributes[1].texCoords.at(texCoordIndex); + } + } + } QHash::const_iterator it = data.indices.find(vertex); if (it == data.indices.constEnd()) { @@ -799,7 +868,7 @@ void appendIndex(MeshData& data, QVector& indices, int index) { data.extracted.mesh.vertices.append(position); data.extracted.mesh.normals.append(normal); data.extracted.mesh.texCoords.append(vertex.texCoord); - + if (hasMoreTexcoords) { data.extracted.mesh.texCoords1.append(vertex.texCoord1); } } else { indices.append(*it); data.extracted.mesh.normals[*it] += normal; @@ -838,13 +907,61 @@ ExtractedMesh extractMesh(const FBXNode& object) { // hack to work around wacky Makehuman exports data.normalsByVertex = true; } - } else if (child.name == "LayerElementUV" && child.properties.at(0).toInt() == 0) { - foreach (const FBXNode& subdata, child.children) { - if (subdata.name == "UV") { - data.texCoords = createVec2Vector(getDoubleVector(subdata)); + } else if (child.name == "LayerElementUV") { + if (child.properties.at(0).toInt() == 0) { + AttributeData attrib; + attrib.index = child.properties.at(0).toInt(); + int unknown = 0; + foreach (const FBXNode& subdata, child.children) { + if (subdata.name == "UV") { + data.texCoords = createVec2Vector(getDoubleVector(subdata)); + attrib.texCoords = createVec2Vector(getDoubleVector(subdata)); + } else if (subdata.name == "UVIndex") { + data.texCoordIndices = getIntVector(subdata); + attrib.texCoordIndices = getIntVector(subdata); + } else if (subdata.name == "Name") { + attrib.name = subdata.properties.at(0).toString().toStdString(); + } else { + std::string subname = subdata.name.data(); + if (subdata.name == "Version") { + } else if (subdata.name == "MappingInformationType") { + } else if (subdata.name == "ReferenceInformationType") { + } else { + unknown++; + } + } + } + data.extracted.texcoordSetMap.insert(QString(attrib.name.c_str()), data.attributes.size()); + data.attributes.push_back(attrib); + } else { + AttributeData attrib; + attrib.index = child.properties.at(0).toInt(); + int unknown = 0; + foreach (const FBXNode& subdata, child.children) { + if (subdata.name == "UV") { + attrib.texCoords = createVec2Vector(getDoubleVector(subdata)); + } else if (subdata.name == "UVIndex") { + attrib.texCoordIndices = getIntVector(subdata); + } else if (subdata.name == "Name") { + attrib.name = subdata.properties.at(0).toString().toStdString(); + } else { + std::string subname = subdata.name.data(); + if (subdata.name == "Version") { + } else if (subdata.name == "MappingInformationType") { + } else if (subdata.name == "ReferenceInformationType") { + } else { + unknown++; + } + } + } - } else if (subdata.name == "UVIndex") { - data.texCoordIndices = getIntVector(subdata); + QHash::iterator it = data.extracted.texcoordSetMap.find(QString(attrib.name.c_str())); + if (it == data.extracted.texcoordSetMap.end()) { + data.extracted.texcoordSetMap.insert(QString(attrib.name.c_str()), data.attributes.size()); + data.attributes.push_back(attrib); + } else { + // WTF same names for different UVs? + unknown = data.attributes.size(); } } } else if (child.name == "LayerElementMaterial") { @@ -1014,11 +1131,27 @@ public: FBXTexture getTexture(const QString& textureID, const QHash& textureNames, const QHash& textureFilenames, - const QHash& textureContent) { + const QHash& textureContent, + const QHash& textureParams) { FBXTexture texture; texture.filename = textureFilenames.value(textureID); texture.name = textureNames.value(textureID); texture.content = textureContent.value(texture.filename); + texture.transform.setIdentity(); + texture.texcoordSet = 0; + QHash::const_iterator it = textureParams.constFind(textureID); + if (it != textureParams.end()) { + const TextureParam& p = (*it); + texture.transform.setTranslation(p.translation); + texture.transform.setRotation(glm::quat(glm::radians(p.rotation))); + texture.transform.setScale(p.scaling); + if (p.UVSet != "map1" || (p.UVSet != "UVSet0")) { + texture.texcoordSet = 1; + } + + texture.texcoordSetName = p.UVSet; + + } return texture; } @@ -1034,6 +1167,23 @@ bool checkMaterialsHaveTextures(const QHash& materials, return false; } +int matchTextureUVSetToAttributeChannel(const std::string& texUVSetName, const QHash& texcoordChannels) { + if (texUVSetName.empty()) { + return 0; + } else { + QHash::const_iterator tcUnit = texcoordChannels.find(QString(texUVSetName.c_str())); + if (tcUnit != texcoordChannels.end()) { + int channel = (*tcUnit); + if (channel >= 2) { + channel = 0; + } + return channel; + } else { + return 0; + } + } +} + FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) { QHash meshes; QHash modelIDsToNames; @@ -1048,6 +1198,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QHash animationCurves; QHash textureNames; QHash textureFilenames; + QHash textureParams; QHash textureContent; QHash materials; QHash typeFlags; @@ -1109,7 +1260,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } } QMultiHash blendshapeChannelIndices; - + int unknown = 0; FBXGeometry geometry; float unitScaleFactor = 1.0f; foreach (const FBXNode& child, node.children) { @@ -1312,7 +1463,10 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) models.insert(getID(object.properties), model); } else if (object.name == "Texture") { + TextureParam tex; + bool texparam = false; foreach (const FBXNode& subobject, object.children) { + std::string subname = subobject.name.data(); if (subobject.name == "RelativeFilename") { // trim off any path information QByteArray filename = subobject.properties.at(0).toByteArray(); @@ -1323,8 +1477,60 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QString name = QString(subobject.properties.at(0).toByteArray()); name = name.left(name.indexOf('[')); textureNames.insert(getID(object.properties), name); + } else if (subobject.name == "Texture_Alpha_Source") { + tex.assign(tex.alphaSource, subobject.properties.at(0).value()); + } else if (subobject.name == "ModelUVTranslation") { + tex.assign(tex.UVTranslation, glm::vec2(subobject.properties.at(0).value(), + subobject.properties.at(1).value())); + } else if (subobject.name == "ModelUVScaling") { + tex.assign(tex.UVScaling, glm::vec2(subobject.properties.at(0).value(), + subobject.properties.at(1).value())); + } else if (subobject.name == "Cropping") { + tex.assign(tex.cropping, glm::vec4(subobject.properties.at(0).value(), + subobject.properties.at(1).value(), + subobject.properties.at(2).value(), + subobject.properties.at(3).value())); + } else if (subobject.name == "Properties70") { + + QByteArray propertyName; + int index; + propertyName = "P"; + index = 4; + foreach (const FBXNode& property, subobject.children) { + if (property.name == propertyName) { + QString v = property.properties.at(0).toString(); + std::string propName = v.toStdString(); + if (property.properties.at(0) == "UVSet") { + tex.assign(tex.UVSet, property.properties.at(index).toString().toStdString()); + } else if (property.properties.at(0) == "CurrentTextureBlendMode") { + tex.assign(tex.currentTextureBlendMode, property.properties.at(index).value()); + } else if (property.properties.at(0) == "UseMaterial") { + tex.assign(tex.useMaterial, property.properties.at(index).value()); + } else if (property.properties.at(0) == "Translation") { + tex.assign(tex.translation, getVec3(property.properties, index)); + } else if (property.properties.at(0) == "Rotation") { + tex.assign(tex.rotation, getVec3(property.properties, index)); + } else if (property.properties.at(0) == "Scaling") { + tex.assign(tex.scaling, getVec3(property.properties, index)); + } else { + unknown++; + } + } + } + } else { + if (subobject.name == "Type") { + } else if (subobject.name == "Version") { + } else if (subobject.name == "FileName") { + } else if (subobject.name == "Media") { + } else { + unknown++; + } } } + + if (!tex.isDefault) { + textureParams.insert(getID(object.properties), tex); + } } else if (object.name == "Video") { QByteArray filename; QByteArray content; @@ -1670,47 +1876,63 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) const QString& childID = children.at(i); if (materials.contains(childID)) { Material material = materials.value(childID); - + bool detectDifferentUVs = false; FBXTexture diffuseTexture; QString diffuseTextureID = diffuseTextures.value(childID); if (!diffuseTextureID.isNull()) { - diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent); + diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent, textureParams); // FBX files generated by 3DSMax have an intermediate texture parent, apparently foreach (const QString& childTextureID, childMap.values(diffuseTextureID)) { if (textureFilenames.contains(childTextureID)) { - diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent); + diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent, textureParams); } } + diffuseTexture.texcoordSet = matchTextureUVSetToAttributeChannel(diffuseTexture.texcoordSetName, extracted.texcoordSetMap); + + detectDifferentUVs = (diffuseTexture.texcoordSet != 0) || (!diffuseTexture.transform.isIdentity()); } FBXTexture normalTexture; QString bumpTextureID = bumpTextures.value(childID); if (!bumpTextureID.isNull()) { - normalTexture = getTexture(bumpTextureID, textureNames, textureFilenames, textureContent); + normalTexture = getTexture(bumpTextureID, textureNames, textureFilenames, textureContent, textureParams); generateTangents = true; + + normalTexture.texcoordSet = matchTextureUVSetToAttributeChannel(normalTexture.texcoordSetName, extracted.texcoordSetMap); + + detectDifferentUVs |= (normalTexture.texcoordSet != 0) || (!normalTexture.transform.isIdentity()); } FBXTexture specularTexture; QString specularTextureID = specularTextures.value(childID); if (!specularTextureID.isNull()) { - specularTexture = getTexture(specularTextureID, textureNames, textureFilenames, textureContent); + specularTexture = getTexture(specularTextureID, textureNames, textureFilenames, textureContent, textureParams); + specularTexture.texcoordSet = matchTextureUVSetToAttributeChannel(specularTexture.texcoordSetName, extracted.texcoordSetMap); + detectDifferentUVs |= (specularTexture.texcoordSet != 0) || (!specularTexture.transform.isIdentity()); } FBXTexture emissiveTexture; QString emissiveTextureID = emissiveTextures.value(childID); if (!emissiveTextureID.isNull()) { - emissiveTexture = getTexture(emissiveTextureID, textureNames, textureFilenames, textureContent); + emissiveTexture = getTexture(emissiveTextureID, textureNames, textureFilenames, textureContent, textureParams); // FBX files generated by 3DSMax have an intermediate texture parent, apparently foreach (const QString& childTextureID, childMap.values(diffuseTextureID)) { if (textureFilenames.contains(childTextureID)) { - emissiveTexture = getTexture(emissiveTextureID, textureNames, textureFilenames, textureContent); + emissiveTexture = getTexture(emissiveTextureID, textureNames, textureFilenames, textureContent, textureParams); } } + + emissiveTexture.texcoordSet = matchTextureUVSetToAttributeChannel(emissiveTexture.texcoordSetName, extracted.texcoordSetMap); + + detectDifferentUVs |= (emissiveTexture.texcoordSet != 0) || (!emissiveTexture.transform.isIdentity()); + } + + if (detectDifferentUVs) { + detectDifferentUVs = false; } - for (int j = 0; j < extracted.partMaterialTextures.size(); j++) { if (extracted.partMaterialTextures.at(j).first == materialIndex) { FBXMeshPart& part = extracted.mesh.parts[j]; @@ -1737,7 +1959,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) materialIndex++; } else if (textureFilenames.contains(childID)) { - FBXTexture texture = getTexture(childID, textureNames, textureFilenames, textureContent); + FBXTexture texture = getTexture(childID, textureNames, textureFilenames, textureContent, textureParams); for (int j = 0; j < extracted.partMaterialTextures.size(); j++) { int partTexture = extracted.partMaterialTextures.at(j).second; if (partTexture == textureIndex && !(partTexture == 0 && materialsHaveTextures)) { diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 892dd7a2fd..60e8ea2448 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -102,6 +103,10 @@ public: QString name; QByteArray filename; QByteArray content; + + Transform transform; + int texcoordSet; + std::string texcoordSetName; }; /// A single part of a mesh (with the same material). @@ -136,13 +141,14 @@ public: QVector tangents; QVector colors; QVector texCoords; + QVector texCoords1; QVector clusterIndices; QVector clusterWeights; QVector clusters; Extents meshExtents; - + bool isEye; QVector blendshapes; diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index 5ce5667a81..4adeccb46f 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -136,7 +136,10 @@ protected: void invalidCache() const { _flags.set(FLAG_CACHE_INVALID, true); } void flagTranslation() { _flags.set(FLAG_TRANSLATION, true); } + void unflagTranslation() { _flags.set(FLAG_TRANSLATION, false); } + void flagRotation() { _flags.set(FLAG_ROTATION, true); } + void unflagRotation() { _flags.set(FLAG_ROTATION, false); } void flagScaling() { _flags.set(FLAG_SCALING, true); } void unflagScaling() { _flags.set(FLAG_SCALING, false); } @@ -162,17 +165,23 @@ inline const Transform::Vec3& Transform::getTranslation() const { inline void Transform::setTranslation(const Vec3& translation) { invalidCache(); - flagTranslation(); + if (translation == Vec3()) { + unflagTranslation(); + } else { + flagTranslation(); + } _translation = translation; } inline void Transform::preTranslate(const Vec3& translation) { + if (translation == Vec3() ) return; invalidCache(); flagTranslation(); _translation += translation; } inline void Transform::postTranslate(const Vec3& translation) { + if (translation == Vec3() ) return; invalidCache(); flagTranslation(); @@ -192,11 +201,16 @@ inline const Transform::Quat& Transform::getRotation() const { inline void Transform::setRotation(const Quat& rotation) { invalidCache(); - flagRotation(); + if (rotation == Quat()) { + unflagRotation(); + } else { + flagRotation(); + } _rotation = rotation; } inline void Transform::preRotate(const Quat& rotation) { + if (rotation == Quat()) return; invalidCache(); if (isRotating()) { _rotation = rotation * _rotation; @@ -204,10 +218,12 @@ inline void Transform::preRotate(const Quat& rotation) { _rotation = rotation; } flagRotation(); + _translation = glm::rotate(rotation, _translation); } inline void Transform::postRotate(const Quat& rotation) { + if (rotation == Quat()) return; invalidCache(); if (isNonUniform()) {