From 06e050f1c181ccd208c97b18b32199d081a72dea Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 29 Oct 2013 22:13:00 -0700 Subject: [PATCH 1/2] Normal map support almost there. --- interface/resources/shaders/model.frag | 6 +- interface/resources/shaders/model.vert | 2 +- .../resources/shaders/model_normal_map.frag | 41 ++++++ .../resources/shaders/model_normal_map.vert | 34 +++++ interface/resources/shaders/skin_model.vert | 2 +- .../shaders/skin_model_normal_map.vert | 50 +++++++ interface/src/renderer/FBXReader.cpp | 31 ++++ interface/src/renderer/FBXReader.h | 1 + interface/src/renderer/GeometryCache.cpp | 16 ++- interface/src/renderer/Model.cpp | 132 +++++++++++++----- interface/src/renderer/Model.h | 20 ++- interface/src/renderer/TextureCache.cpp | 23 ++- interface/src/renderer/TextureCache.h | 4 + 13 files changed, 308 insertions(+), 54 deletions(-) create mode 100644 interface/resources/shaders/model_normal_map.frag create mode 100644 interface/resources/shaders/model_normal_map.vert create mode 100644 interface/resources/shaders/skin_model_normal_map.vert diff --git a/interface/resources/shaders/model.frag b/interface/resources/shaders/model.frag index 12b81485e5..877cdca885 100644 --- a/interface/resources/shaders/model.frag +++ b/interface/resources/shaders/model.frag @@ -1,7 +1,7 @@ #version 120 // -// blendface.frag +// model.frag // fragment shader // // Created by Andrzej Kapolka on 10/14/13. @@ -9,7 +9,7 @@ // // the diffuse texture -uniform sampler2D texture; +uniform sampler2D diffuseMap; // the interpolated normal varying vec4 normal; @@ -24,6 +24,6 @@ void main(void) { float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), normalizedNormal)); // modulate texture by base color and add specular contribution - gl_FragColor = base * texture2D(texture, gl_TexCoord[0].st) + + gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular; } diff --git a/interface/resources/shaders/model.vert b/interface/resources/shaders/model.vert index b9c1a35e11..d07a657d25 100644 --- a/interface/resources/shaders/model.vert +++ b/interface/resources/shaders/model.vert @@ -1,7 +1,7 @@ #version 120 // -// blendface.vert +// model.vert // vertex shader // // Created by Andrzej Kapolka on 10/14/13. diff --git a/interface/resources/shaders/model_normal_map.frag b/interface/resources/shaders/model_normal_map.frag new file mode 100644 index 0000000000..9740a4d4b1 --- /dev/null +++ b/interface/resources/shaders/model_normal_map.frag @@ -0,0 +1,41 @@ +#version 120 + +// +// model_normal_map.frag +// fragment shader +// +// Created by Andrzej Kapolka on 10/29/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +// the diffuse texture +uniform sampler2D diffuseMap; + +// the normal map texture +uniform sampler2D normalMap; + +// the interpolated normal +varying vec4 interpolatedNormal; + +// the interpolated tangent +varying vec4 interpolatedTangent; + +void main(void) { + vec3 normalizedNormal = normalize(vec3(interpolatedNormal)); + vec3 normalizedTangent = normalize(vec3(interpolatedTangent)); + vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent)); + vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) * 2.0 - vec3(1.0, 1.0, 1.0); + + // compute the base color based on OpenGL lighting model + vec4 viewNormal = vec4(normalizedTangent * localNormal.x + + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); + vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + + gl_FrontLightProduct[0].diffuse * max(0.0, dot(viewNormal, gl_LightSource[0].position))); + + // compute the specular component (sans exponent) + float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), viewNormal)); + + // modulate texture by base color and add specular contribution + gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + + pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular; +} diff --git a/interface/resources/shaders/model_normal_map.vert b/interface/resources/shaders/model_normal_map.vert new file mode 100644 index 0000000000..c367a30e06 --- /dev/null +++ b/interface/resources/shaders/model_normal_map.vert @@ -0,0 +1,34 @@ +#version 120 + +// +// model.vert +// vertex shader +// +// Created by Andrzej Kapolka on 10/14/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +// the tangent vector +attribute vec3 tangent; + +// the interpolated normal +varying vec4 interpolatedNormal; + +// the interpolated tangent +varying vec4 interpolatedTangent; + +void main(void) { + + // transform and store the normal and tangent for interpolation + interpolatedNormal = gl_ModelViewMatrix * vec4(gl_Normal, 0.0); + interpolatedTangent = gl_ModelViewMatrix * vec4(tangent, 0.0); + + // pass along the vertex color + gl_FrontColor = gl_Color; + + // and the texture coordinates + gl_TexCoord[0] = gl_MultiTexCoord0; + + // use standard pipeline transform + gl_Position = ftransform(); +} diff --git a/interface/resources/shaders/skin_model.vert b/interface/resources/shaders/skin_model.vert index 2d1b98c4d7..f76c766085 100644 --- a/interface/resources/shaders/skin_model.vert +++ b/interface/resources/shaders/skin_model.vert @@ -1,7 +1,7 @@ #version 120 // -// skin_blendface.vert +// skin_model.vert // vertex shader // // Created by Andrzej Kapolka on 10/14/13. diff --git a/interface/resources/shaders/skin_model_normal_map.vert b/interface/resources/shaders/skin_model_normal_map.vert new file mode 100644 index 0000000000..0779aa3d9a --- /dev/null +++ b/interface/resources/shaders/skin_model_normal_map.vert @@ -0,0 +1,50 @@ +#version 120 + +// +// skin_model_normal_map.vert +// vertex shader +// +// Created by Andrzej Kapolka on 10/29/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +const int MAX_CLUSTERS = 64; +const int INDICES_PER_VERTEX = 4; + +uniform mat4 clusterMatrices[MAX_CLUSTERS]; + +// the tangent vector +attribute vec3 tangent; + +attribute vec4 clusterIndices; +attribute vec4 clusterWeights; + +// the interpolated normal +varying vec4 interpolatedNormal; + +// the interpolated tangent +varying vec4 interpolatedTangent; + +void main(void) { + vec4 position = vec4(0.0, 0.0, 0.0, 0.0); + interpolatedNormal = vec4(0.0, 0.0, 0.0, 0.0); + interpolatedTangent = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[int(clusterIndices[i])]; + float clusterWeight = clusterWeights[i]; + position += clusterMatrix * gl_Vertex * clusterWeight; + interpolatedNormal += clusterMatrix * vec4(gl_Normal, 0.0) * clusterWeight; + interpolatedTangent += clusterMatrix * vec4(tangent, 0.0) * clusterWeight; + } + position = gl_ModelViewProjectionMatrix * position; + interpolatedNormal = gl_ModelViewMatrix * interpolatedNormal; + interpolatedTangent = gl_ModelViewMatrix * interpolatedTangent; + + // pass along the vertex color + gl_FrontColor = vec4(1.0, 1.0, 1.0, 1.0); + + // and the texture coordinates + gl_TexCoord[0] = gl_MultiTexCoord0; + + gl_Position = position; +} diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index f03ca495a9..9988dd855b 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -677,6 +677,17 @@ FBXMesh extractMesh(const FBXNode& object) { return mesh; } +void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) { + glm::vec3 normal = mesh.normals.at(firstIndex); + glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex)); + if (glm::length(bitangent) < EPSILON) { + return; + } + glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex); + mesh.tangents[firstIndex] += glm::cross(glm::angleAxis( + -glm::degrees(atan2f(texCoordDelta.t, texCoordDelta.s)), normal) * glm::normalize(bitangent), normal); +} + FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) { QHash meshes; QVector blendshapes; @@ -1054,6 +1065,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // look for textures, material properties int partIndex = mesh.parts.size() - 1; + bool generateTangents = false; foreach (const QString& childID, childMap.values(modelID)) { if (partIndex < 0) { break; @@ -1084,10 +1096,29 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QString bumpTextureID = bumpTextures.value(childID); if (!bumpTextureID.isNull()) { part.normalFilename = textureFilenames.value(bumpTextureID); + generateTangents = true; } partIndex--; } + // if we have a normal map (and texture coordinates), we must compute tangents + if (generateTangents && !mesh.texCoords.isEmpty()) { + mesh.tangents.resize(mesh.vertices.size()); + foreach (const FBXMeshPart& part, mesh.parts) { + for (int i = 0; i < part.quadIndices.size(); i += 4) { + setTangents(mesh, part.quadIndices.at(i), part.quadIndices.at(i + 1)); + setTangents(mesh, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2)); + setTangents(mesh, part.quadIndices.at(i + 2), part.quadIndices.at(i + 3)); + setTangents(mesh, part.quadIndices.at(i + 3), part.quadIndices.at(i)); + } + for (int i = 0; i < part.triangleIndices.size(); i += 3) { + setTangents(mesh, part.triangleIndices.at(i), part.triangleIndices.at(i + 1)); + setTangents(mesh, part.triangleIndices.at(i + 1), part.triangleIndices.at(i + 2)); + setTangents(mesh, part.triangleIndices.at(i + 2), part.triangleIndices.at(i)); + } + } + } + // find the clusters with which the mesh is associated mesh.isEye = false; QVector clusterIDs; diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index c48da019ae..69fec1cda9 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -86,6 +86,7 @@ public: QVector vertices; QVector normals; + QVector tangents; QVector colors; QVector texCoords; QVector clusterIndices; diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 7e85c1c895..35d2ef1b09 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -396,7 +396,8 @@ void NetworkGeometry::maybeReadModelWithMapping() { // if we don't need to do any blending or springing, then the positions/normals can be static if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); - int colorsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); + 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 clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); @@ -404,6 +405,7 @@ void NetworkGeometry::maybeReadModelWithMapping() { NULL, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.vertices.size() * sizeof(glm::vec3), mesh.vertices.constData()); glBufferSubData(GL_ARRAY_BUFFER, normalsOffset, mesh.normals.size() * sizeof(glm::vec3), mesh.normals.constData()); + glBufferSubData(GL_ARRAY_BUFFER, tangentsOffset, mesh.tangents.size() * sizeof(glm::vec3), mesh.tangents.constData()); glBufferSubData(GL_ARRAY_BUFFER, colorsOffset, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData()); glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData()); @@ -414,12 +416,14 @@ void NetworkGeometry::maybeReadModelWithMapping() { // if there's no springiness, then the cluster indices/weights can be static } else if (mesh.springiness == 0.0f) { - int texCoordsOffset = mesh.colors.size() * sizeof(glm::vec3); + int colorsOffset = 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 clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); glBufferData(GL_ARRAY_BUFFER, clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4), NULL, GL_STATIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData()); + glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.tangents.size() * sizeof(glm::vec3), mesh.tangents.constData()); + glBufferSubData(GL_ARRAY_BUFFER, colorsOffset, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData()); glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData()); glBufferSubData(GL_ARRAY_BUFFER, clusterIndicesOffset, mesh.clusterIndices.size() * sizeof(glm::vec4), mesh.clusterIndices.constData()); @@ -427,9 +431,11 @@ void NetworkGeometry::maybeReadModelWithMapping() { mesh.clusterWeights.constData()); } else { - int texCoordsOffset = mesh.colors.size() * sizeof(glm::vec3); + int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); glBufferData(GL_ARRAY_BUFFER, texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2), NULL, GL_STATIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData()); + glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.tangents.size() * sizeof(glm::vec3), mesh.tangents.constData()); + glBufferSubData(GL_ARRAY_BUFFER, colorsOffset, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData()); glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData()); } diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 7ba8d24104..4deb886ddb 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -26,10 +26,23 @@ Model::~Model() { } ProgramObject Model::_program; +ProgramObject Model::_normalMapProgram; ProgramObject Model::_skinProgram; -int Model::_clusterMatricesLocation; -int Model::_clusterIndicesLocation; -int Model::_clusterWeightsLocation; +ProgramObject Model::_skinNormalMapProgram; +int Model::_normalMapTangentLocation; +Model::SkinLocations Model::_skinLocations; +Model::SkinLocations Model::_skinNormalMapLocations; + +void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations) { + program.bind(); + locations.clusterMatrices = program.uniformLocation("clusterMatrices"); + locations.clusterIndices = program.attributeLocation("clusterIndices"); + locations.clusterWeights = program.attributeLocation("clusterWeights"); + locations.tangent = program.attributeLocation("tangent"); + program.setUniformValue("diffuseMap", 0); + program.setUniformValue("normalMap", 1); + program.release(); +} void Model::init() { if (!_program.isLinked()) { @@ -42,16 +55,27 @@ void Model::init() { _program.setUniformValue("texture", 0); _program.release(); + _normalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/model_normal_map.vert"); + _normalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/model_normal_map.frag"); + _normalMapProgram.link(); + + _normalMapProgram.bind(); + _normalMapProgram.setUniformValue("diffuseMap", 0); + _normalMapProgram.setUniformValue("normalMap", 1); + _normalMapTangentLocation = _normalMapProgram.attributeLocation("tangent"); + _normalMapProgram.release(); + _skinProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/skin_model.vert"); _skinProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/model.frag"); _skinProgram.link(); - _skinProgram.bind(); - _clusterMatricesLocation = _skinProgram.uniformLocation("clusterMatrices"); - _clusterIndicesLocation = _skinProgram.attributeLocation("clusterIndices"); - _clusterWeightsLocation = _skinProgram.attributeLocation("clusterWeights"); - _skinProgram.setUniformValue("texture", 0); - _skinProgram.release(); + initSkinProgram(_skinProgram, _skinLocations); + + _skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/skin_model_normal_map.vert"); + _skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/model_normal_map.frag"); + _skinNormalMapProgram.link(); + + initSkinProgram(_skinNormalMapProgram, _skinNormalMapLocations); } } @@ -245,38 +269,61 @@ bool Model::render(float alpha) { int vertexCount = mesh.vertices.size(); glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); + + ProgramObject* program = &_program; + ProgramObject* skinProgram = &_skinProgram; + SkinLocations* skinLocations = &_skinLocations; + if (!mesh.tangents.isEmpty()) { + program = &_normalMapProgram; + skinProgram = &_skinNormalMapProgram; + skinLocations = &_skinNormalMapLocations; + } const MeshState& state = _meshStates.at(i); + ProgramObject* activeProgram = program; + int tangentLocation = _normalMapTangentLocation; if (state.worldSpaceVertices.isEmpty()) { if (state.clusterMatrices.size() > 1) { - _skinProgram.bind(); - glUniformMatrix4fvARB(_clusterMatricesLocation, state.clusterMatrices.size(), false, + skinProgram->bind(); + glUniformMatrix4fvARB(skinLocations->clusterMatrices, state.clusterMatrices.size(), false, (const float*)state.clusterMatrices.constData()); - int offset = mesh.colors.size() * sizeof(glm::vec3) + mesh.texCoords.size() * sizeof(glm::vec2) + + int offset = (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3) + + mesh.texCoords.size() * sizeof(glm::vec2) + (mesh.blendshapes.isEmpty() ? vertexCount * 2 * sizeof(glm::vec3) : 0); - _skinProgram.setAttributeBuffer(_clusterIndicesLocation, GL_FLOAT, offset, 4); - _skinProgram.setAttributeBuffer(_clusterWeightsLocation, GL_FLOAT, + skinProgram->setAttributeBuffer(skinLocations->clusterIndices, GL_FLOAT, offset, 4); + skinProgram->setAttributeBuffer(skinLocations->clusterWeights, GL_FLOAT, offset + vertexCount * sizeof(glm::vec4), 4); - _skinProgram.enableAttributeArray(_clusterIndicesLocation); - _skinProgram.enableAttributeArray(_clusterWeightsLocation); - + skinProgram->enableAttributeArray(skinLocations->clusterIndices); + skinProgram->enableAttributeArray(skinLocations->clusterWeights); + activeProgram = skinProgram; + tangentLocation = skinLocations->tangent; + } else { glPushMatrix(); glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]); - _program.bind(); + program->bind(); } } else { - _program.bind(); + program->bind(); } - + if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { - glColorPointer(3, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3))); + if (!mesh.tangents.isEmpty()) { + activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3); + activeProgram->enableAttributeArray(tangentLocation); + } + glColorPointer(3, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + + mesh.tangents.size() * sizeof(glm::vec3))); glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + - mesh.colors.size() * sizeof(glm::vec3))); + (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); } else { - glColorPointer(3, GL_FLOAT, 0, 0); - glTexCoordPointer(2, GL_FLOAT, 0, (void*)(mesh.colors.size() * sizeof(glm::vec3))); + if (!mesh.tangents.isEmpty()) { + activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, 0, 3); + activeProgram->enableAttributeArray(tangentLocation); + } + glColorPointer(3, GL_FLOAT, 0, (void*)(mesh.tangents.size() * sizeof(glm::vec3))); + glTexCoordPointer(2, GL_FLOAT, 0, (void*)((mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); glBindBuffer(GL_ARRAY_BUFFER, _blendedVertexBufferIDs.at(i)); if (!state.worldSpaceVertices.isEmpty()) { @@ -337,15 +384,23 @@ bool Model::render(float alpha) { glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); glMaterialf(GL_FRONT, GL_SHININESS, part.shininess); - Texture* texture = networkPart.diffuseTexture.data(); + Texture* diffuseMap = networkPart.diffuseTexture.data(); if (mesh.isEye) { - if (texture != NULL) { - texture = (_dilatedTextures[i][j] = static_cast(texture)->getDilatedTexture( - _pupilDilation)).data(); + if (diffuseMap != NULL) { + diffuseMap = (_dilatedTextures[i][j] = + static_cast(diffuseMap)->getDilatedTexture(_pupilDilation)).data(); } } - glBindTexture(GL_TEXTURE_2D, texture == NULL ? Application::getInstance()->getTextureCache()->getWhiteTextureID() : - texture->getID()); + glBindTexture(GL_TEXTURE_2D, diffuseMap == NULL ? + Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID()); + + if (!mesh.tangents.isEmpty()) { + glActiveTexture(GL_TEXTURE1); + Texture* normalMap = networkPart.normalTexture.data(); + glBindTexture(GL_TEXTURE_2D, normalMap == NULL ? + Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID()); + glActiveTexture(GL_TEXTURE0); + } glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset); offset += part.quadIndices.size() * sizeof(int); @@ -361,19 +416,24 @@ bool Model::render(float alpha) { glDisableClientState(GL_TEXTURE_COORD_ARRAY); } + if (!mesh.tangents.isEmpty()) { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + + activeProgram->disableAttributeArray(tangentLocation); + } + if (state.worldSpaceVertices.isEmpty()) { if (state.clusterMatrices.size() > 1) { - _skinProgram.disableAttributeArray(_clusterIndicesLocation); - _skinProgram.disableAttributeArray(_clusterWeightsLocation); - _skinProgram.release(); + skinProgram->disableAttributeArray(skinLocations->clusterIndices); + skinProgram->disableAttributeArray(skinLocations->clusterWeights); } else { glPopMatrix(); - _program.release(); } - } else { - _program.release(); } + activeProgram->release(); } // deactivate vertex arrays after drawing diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 4b54fcaaf4..f485a35da0 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -148,10 +148,24 @@ private: QVector _attachments; static ProgramObject _program; + static ProgramObject _normalMapProgram; static ProgramObject _skinProgram; - static int _clusterMatricesLocation; - static int _clusterIndicesLocation; - static int _clusterWeightsLocation; + static ProgramObject _skinNormalMapProgram; + + static int _normalMapTangentLocation; + + class SkinLocations { + public: + int clusterMatrices; + int clusterIndices; + int clusterWeights; + int tangent; + }; + + static SkinLocations _skinLocations; + static SkinLocations _skinNormalMapLocations; + + static void initSkinProgram(ProgramObject& program, SkinLocations& locations); }; #endif /* defined(__interface__Model__) */ diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index c24f1fad16..959ba009a5 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -20,6 +20,7 @@ TextureCache::TextureCache() : _permutationNormalTextureID(0), _whiteTextureID(0), + _blueTextureID(0), _primaryFramebufferObject(NULL), _secondaryFramebufferObject(NULL), _tertiaryFramebufferObject(NULL) @@ -74,9 +75,11 @@ GLuint TextureCache::getPermutationNormalTextureID() { return _permutationNormalTextureID; } -static void loadWhiteTexture() { - const char OPAQUE_WHITE[] = { 0xFF, 0xFF, 0xFF, 0xFF }; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, OPAQUE_WHITE); +const char OPAQUE_WHITE[] = { 0xFF, 0xFF, 0xFF, 0xFF }; +const char OPAQUE_BLUE[] = { 0x80, 0x80, 0xFF, 0xFF }; + +static void loadSingleColorTexture(const char* color) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, color); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } @@ -84,12 +87,22 @@ GLuint TextureCache::getWhiteTextureID() { if (_whiteTextureID == 0) { glGenTextures(1, &_whiteTextureID); glBindTexture(GL_TEXTURE_2D, _whiteTextureID); - loadWhiteTexture(); + loadSingleColorTexture(OPAQUE_WHITE); glBindTexture(GL_TEXTURE_2D, 0); } return _whiteTextureID; } +GLuint TextureCache::getBlueTextureID() { + if (_blueTextureID == 0) { + glGenTextures(1, &_blueTextureID); + glBindTexture(GL_TEXTURE_2D, _blueTextureID); + loadSingleColorTexture(OPAQUE_BLUE); + glBindTexture(GL_TEXTURE_2D, 0); + } + return _blueTextureID; +} + GLuint TextureCache::getFileTextureID(const QString& filename) { GLuint id = _fileTextureIDs.value(filename); if (id == 0) { @@ -219,7 +232,7 @@ NetworkTexture::NetworkTexture(const QUrl& url) : _reply(NULL), _averageColor(1. // default to white glBindTexture(GL_TEXTURE_2D, getID()); - loadWhiteTexture(); + loadSingleColorTexture(OPAQUE_WHITE); glBindTexture(GL_TEXTURE_2D, 0); } diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index 7ebd4528fb..cd15f67b8e 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -40,6 +40,9 @@ public: /// Returns the ID of an opaque white texture (useful for a default). GLuint getWhiteTextureID(); + /// Returns the ID of a pale blue texture (useful for a normal map). + GLuint getBlueTextureID(); + /// Returns the ID of a texture containing the contents of the specified file, loading it if necessary. GLuint getFileTextureID(const QString& filename); @@ -69,6 +72,7 @@ private: GLuint _permutationNormalTextureID; GLuint _whiteTextureID; + GLuint _blueTextureID; QHash _fileTextureIDs; From 156b3dd2e66de4be9e4cf3e73f306af328f69bec Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 30 Oct 2013 11:55:22 -0700 Subject: [PATCH 2/2] Sign change. Forgot we flip the texture coordinates. --- interface/src/renderer/FBXReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 9988dd855b..872577d2fa 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -685,7 +685,7 @@ void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) { } glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex); mesh.tangents[firstIndex] += glm::cross(glm::angleAxis( - -glm::degrees(atan2f(texCoordDelta.t, texCoordDelta.s)), normal) * glm::normalize(bitangent), normal); + -glm::degrees(atan2f(-texCoordDelta.t, texCoordDelta.s)), normal) * glm::normalize(bitangent), normal); } FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {