Merge pull request #1147 from ey6es/ick

Support for normap maps on loaded models.
This commit is contained in:
ZappoMan 2013-10-30 12:26:56 -07:00
commit e9d366fb80
13 changed files with 308 additions and 54 deletions

View file

@ -1,7 +1,7 @@
#version 120 #version 120
// //
// blendface.frag // model.frag
// fragment shader // fragment shader
// //
// Created by Andrzej Kapolka on 10/14/13. // Created by Andrzej Kapolka on 10/14/13.
@ -9,7 +9,7 @@
// //
// the diffuse texture // the diffuse texture
uniform sampler2D texture; uniform sampler2D diffuseMap;
// the interpolated normal // the interpolated normal
varying vec4 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)); 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 // 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; pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular;
} }

View file

@ -1,7 +1,7 @@
#version 120 #version 120
// //
// blendface.vert // model.vert
// vertex shader // vertex shader
// //
// Created by Andrzej Kapolka on 10/14/13. // Created by Andrzej Kapolka on 10/14/13.

View file

@ -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;
}

View file

@ -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();
}

View file

@ -1,7 +1,7 @@
#version 120 #version 120
// //
// skin_blendface.vert // skin_model.vert
// vertex shader // vertex shader
// //
// Created by Andrzej Kapolka on 10/14/13. // Created by Andrzej Kapolka on 10/14/13.

View file

@ -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;
}

View file

@ -677,6 +677,17 @@ FBXMesh extractMesh(const FBXNode& object) {
return mesh; 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) { FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
QHash<QString, FBXMesh> meshes; QHash<QString, FBXMesh> meshes;
QVector<ExtractedBlendshape> blendshapes; QVector<ExtractedBlendshape> blendshapes;
@ -1054,6 +1065,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
// look for textures, material properties // look for textures, material properties
int partIndex = mesh.parts.size() - 1; int partIndex = mesh.parts.size() - 1;
bool generateTangents = false;
foreach (const QString& childID, childMap.values(modelID)) { foreach (const QString& childID, childMap.values(modelID)) {
if (partIndex < 0) { if (partIndex < 0) {
break; break;
@ -1084,10 +1096,29 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
QString bumpTextureID = bumpTextures.value(childID); QString bumpTextureID = bumpTextures.value(childID);
if (!bumpTextureID.isNull()) { if (!bumpTextureID.isNull()) {
part.normalFilename = textureFilenames.value(bumpTextureID); part.normalFilename = textureFilenames.value(bumpTextureID);
generateTangents = true;
} }
partIndex--; 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 // find the clusters with which the mesh is associated
mesh.isEye = false; mesh.isEye = false;
QVector<QString> clusterIDs; QVector<QString> clusterIDs;

View file

@ -86,6 +86,7 @@ public:
QVector<glm::vec3> vertices; QVector<glm::vec3> vertices;
QVector<glm::vec3> normals; QVector<glm::vec3> normals;
QVector<glm::vec3> tangents;
QVector<glm::vec3> colors; QVector<glm::vec3> colors;
QVector<glm::vec2> texCoords; QVector<glm::vec2> texCoords;
QVector<glm::vec4> clusterIndices; QVector<glm::vec4> clusterIndices;

View file

@ -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 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) { if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) {
int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); 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 texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3);
int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2);
int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4);
@ -404,6 +405,7 @@ void NetworkGeometry::maybeReadModelWithMapping() {
NULL, GL_STATIC_DRAW); NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.vertices.size() * sizeof(glm::vec3), mesh.vertices.constData()); 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, 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, colorsOffset, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData());
glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2), glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2),
mesh.texCoords.constData()); mesh.texCoords.constData());
@ -414,12 +416,14 @@ void NetworkGeometry::maybeReadModelWithMapping() {
// if there's no springiness, then the cluster indices/weights can be static // if there's no springiness, then the cluster indices/weights can be static
} else if (mesh.springiness == 0.0f) { } 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 clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2);
int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4);
glBufferData(GL_ARRAY_BUFFER, clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4), glBufferData(GL_ARRAY_BUFFER, clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4),
NULL, GL_STATIC_DRAW); 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, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData());
glBufferSubData(GL_ARRAY_BUFFER, clusterIndicesOffset, mesh.clusterIndices.size() * sizeof(glm::vec4), glBufferSubData(GL_ARRAY_BUFFER, clusterIndicesOffset, mesh.clusterIndices.size() * sizeof(glm::vec4),
mesh.clusterIndices.constData()); mesh.clusterIndices.constData());
@ -427,9 +431,11 @@ void NetworkGeometry::maybeReadModelWithMapping() {
mesh.clusterWeights.constData()); mesh.clusterWeights.constData());
} else { } 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); 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), glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2),
mesh.texCoords.constData()); mesh.texCoords.constData());
} }

View file

@ -26,10 +26,23 @@ Model::~Model() {
} }
ProgramObject Model::_program; ProgramObject Model::_program;
ProgramObject Model::_normalMapProgram;
ProgramObject Model::_skinProgram; ProgramObject Model::_skinProgram;
int Model::_clusterMatricesLocation; ProgramObject Model::_skinNormalMapProgram;
int Model::_clusterIndicesLocation; int Model::_normalMapTangentLocation;
int Model::_clusterWeightsLocation; 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() { void Model::init() {
if (!_program.isLinked()) { if (!_program.isLinked()) {
@ -42,16 +55,27 @@ void Model::init() {
_program.setUniformValue("texture", 0); _program.setUniformValue("texture", 0);
_program.release(); _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::Vertex, "resources/shaders/skin_model.vert");
_skinProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/model.frag"); _skinProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/model.frag");
_skinProgram.link(); _skinProgram.link();
_skinProgram.bind(); initSkinProgram(_skinProgram, _skinLocations);
_clusterMatricesLocation = _skinProgram.uniformLocation("clusterMatrices");
_clusterIndicesLocation = _skinProgram.attributeLocation("clusterIndices"); _skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/skin_model_normal_map.vert");
_clusterWeightsLocation = _skinProgram.attributeLocation("clusterWeights"); _skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/model_normal_map.frag");
_skinProgram.setUniformValue("texture", 0); _skinNormalMapProgram.link();
_skinProgram.release();
initSkinProgram(_skinNormalMapProgram, _skinNormalMapLocations);
} }
} }
@ -246,37 +270,60 @@ bool Model::render(float alpha) {
glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); 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); const MeshState& state = _meshStates.at(i);
ProgramObject* activeProgram = program;
int tangentLocation = _normalMapTangentLocation;
if (state.worldSpaceVertices.isEmpty()) { if (state.worldSpaceVertices.isEmpty()) {
if (state.clusterMatrices.size() > 1) { if (state.clusterMatrices.size() > 1) {
_skinProgram.bind(); skinProgram->bind();
glUniformMatrix4fvARB(_clusterMatricesLocation, state.clusterMatrices.size(), false, glUniformMatrix4fvARB(skinLocations->clusterMatrices, state.clusterMatrices.size(), false,
(const float*)state.clusterMatrices.constData()); (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); (mesh.blendshapes.isEmpty() ? vertexCount * 2 * sizeof(glm::vec3) : 0);
_skinProgram.setAttributeBuffer(_clusterIndicesLocation, GL_FLOAT, offset, 4); skinProgram->setAttributeBuffer(skinLocations->clusterIndices, GL_FLOAT, offset, 4);
_skinProgram.setAttributeBuffer(_clusterWeightsLocation, GL_FLOAT, skinProgram->setAttributeBuffer(skinLocations->clusterWeights, GL_FLOAT,
offset + vertexCount * sizeof(glm::vec4), 4); offset + vertexCount * sizeof(glm::vec4), 4);
_skinProgram.enableAttributeArray(_clusterIndicesLocation); skinProgram->enableAttributeArray(skinLocations->clusterIndices);
_skinProgram.enableAttributeArray(_clusterWeightsLocation); skinProgram->enableAttributeArray(skinLocations->clusterWeights);
activeProgram = skinProgram;
tangentLocation = skinLocations->tangent;
} else { } else {
glPushMatrix(); glPushMatrix();
glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]); glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]);
_program.bind(); program->bind();
} }
} else { } else {
_program.bind(); program->bind();
} }
if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { 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) + 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 { } else {
glColorPointer(3, GL_FLOAT, 0, 0); if (!mesh.tangents.isEmpty()) {
glTexCoordPointer(2, GL_FLOAT, 0, (void*)(mesh.colors.size() * sizeof(glm::vec3))); 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)); glBindBuffer(GL_ARRAY_BUFFER, _blendedVertexBufferIDs.at(i));
if (!state.worldSpaceVertices.isEmpty()) { if (!state.worldSpaceVertices.isEmpty()) {
@ -337,15 +384,23 @@ bool Model::render(float alpha) {
glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular);
glMaterialf(GL_FRONT, GL_SHININESS, part.shininess); glMaterialf(GL_FRONT, GL_SHININESS, part.shininess);
Texture* texture = networkPart.diffuseTexture.data(); Texture* diffuseMap = networkPart.diffuseTexture.data();
if (mesh.isEye) { if (mesh.isEye) {
if (texture != NULL) { if (diffuseMap != NULL) {
texture = (_dilatedTextures[i][j] = static_cast<DilatableNetworkTexture*>(texture)->getDilatedTexture( diffuseMap = (_dilatedTextures[i][j] =
_pupilDilation)).data(); static_cast<DilatableNetworkTexture*>(diffuseMap)->getDilatedTexture(_pupilDilation)).data();
} }
} }
glBindTexture(GL_TEXTURE_2D, texture == NULL ? Application::getInstance()->getTextureCache()->getWhiteTextureID() : glBindTexture(GL_TEXTURE_2D, diffuseMap == NULL ?
texture->getID()); 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); glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset);
offset += part.quadIndices.size() * sizeof(int); offset += part.quadIndices.size() * sizeof(int);
@ -361,19 +416,24 @@ bool Model::render(float alpha) {
glDisableClientState(GL_TEXTURE_COORD_ARRAY); 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.worldSpaceVertices.isEmpty()) {
if (state.clusterMatrices.size() > 1) { if (state.clusterMatrices.size() > 1) {
_skinProgram.disableAttributeArray(_clusterIndicesLocation); skinProgram->disableAttributeArray(skinLocations->clusterIndices);
_skinProgram.disableAttributeArray(_clusterWeightsLocation); skinProgram->disableAttributeArray(skinLocations->clusterWeights);
_skinProgram.release();
} else { } else {
glPopMatrix(); glPopMatrix();
_program.release();
} }
} else {
_program.release();
} }
activeProgram->release();
} }
// deactivate vertex arrays after drawing // deactivate vertex arrays after drawing

View file

@ -148,10 +148,24 @@ private:
QVector<Model*> _attachments; QVector<Model*> _attachments;
static ProgramObject _program; static ProgramObject _program;
static ProgramObject _normalMapProgram;
static ProgramObject _skinProgram; static ProgramObject _skinProgram;
static int _clusterMatricesLocation; static ProgramObject _skinNormalMapProgram;
static int _clusterIndicesLocation;
static int _clusterWeightsLocation; 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__) */ #endif /* defined(__interface__Model__) */

View file

@ -20,6 +20,7 @@
TextureCache::TextureCache() : TextureCache::TextureCache() :
_permutationNormalTextureID(0), _permutationNormalTextureID(0),
_whiteTextureID(0), _whiteTextureID(0),
_blueTextureID(0),
_primaryFramebufferObject(NULL), _primaryFramebufferObject(NULL),
_secondaryFramebufferObject(NULL), _secondaryFramebufferObject(NULL),
_tertiaryFramebufferObject(NULL) _tertiaryFramebufferObject(NULL)
@ -74,9 +75,11 @@ GLuint TextureCache::getPermutationNormalTextureID() {
return _permutationNormalTextureID; return _permutationNormalTextureID;
} }
static void loadWhiteTexture() { const char OPAQUE_WHITE[] = { 0xFF, 0xFF, 0xFF, 0xFF };
const char OPAQUE_WHITE[] = { 0xFF, 0xFF, 0xFF, 0xFF }; const char OPAQUE_BLUE[] = { 0x80, 0x80, 0xFF, 0xFF };
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, OPAQUE_WHITE);
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); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
} }
@ -84,12 +87,22 @@ GLuint TextureCache::getWhiteTextureID() {
if (_whiteTextureID == 0) { if (_whiteTextureID == 0) {
glGenTextures(1, &_whiteTextureID); glGenTextures(1, &_whiteTextureID);
glBindTexture(GL_TEXTURE_2D, _whiteTextureID); glBindTexture(GL_TEXTURE_2D, _whiteTextureID);
loadWhiteTexture(); loadSingleColorTexture(OPAQUE_WHITE);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }
return _whiteTextureID; 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 TextureCache::getFileTextureID(const QString& filename) {
GLuint id = _fileTextureIDs.value(filename); GLuint id = _fileTextureIDs.value(filename);
if (id == 0) { if (id == 0) {
@ -219,7 +232,7 @@ NetworkTexture::NetworkTexture(const QUrl& url) : _reply(NULL), _averageColor(1.
// default to white // default to white
glBindTexture(GL_TEXTURE_2D, getID()); glBindTexture(GL_TEXTURE_2D, getID());
loadWhiteTexture(); loadSingleColorTexture(OPAQUE_WHITE);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }

View file

@ -40,6 +40,9 @@ public:
/// Returns the ID of an opaque white texture (useful for a default). /// Returns the ID of an opaque white texture (useful for a default).
GLuint getWhiteTextureID(); 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. /// Returns the ID of a texture containing the contents of the specified file, loading it if necessary.
GLuint getFileTextureID(const QString& filename); GLuint getFileTextureID(const QString& filename);
@ -69,6 +72,7 @@ private:
GLuint _permutationNormalTextureID; GLuint _permutationNormalTextureID;
GLuint _whiteTextureID; GLuint _whiteTextureID;
GLuint _blueTextureID;
QHash<QString, GLuint> _fileTextureIDs; QHash<QString, GLuint> _fileTextureIDs;