Merge pull request #1112 from ey6es/attach

Support for skeleton attachments (FBX meshes or SVO voxel trees).
This commit is contained in:
ZappoMan 2013-10-24 14:35:11 -07:00
commit 75638f35b1
9 changed files with 278 additions and 36 deletions

View file

@ -17,8 +17,8 @@ varying vec4 normal;
void main(void) { void main(void) {
// compute the base color based on OpenGL lighting model // compute the base color based on OpenGL lighting model
vec4 normalizedNormal = normalize(normal); vec4 normalizedNormal = normalize(normal);
vec4 base = gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[0].position)); gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[0].position)));
// compute the specular component (sans exponent) // 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)), normalizedNormal)); float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), normalizedNormal));

View file

@ -16,7 +16,10 @@ void main(void) {
// transform and store the normal for interpolation // transform and store the normal for interpolation
normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
// pass along the texture coordinate // pass along the vertex color
gl_FrontColor = gl_Color;
// and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0; gl_TexCoord[0] = gl_MultiTexCoord0;
// use standard pipeline transform // use standard pipeline transform

View file

@ -31,7 +31,10 @@ void main(void) {
position = gl_ModelViewProjectionMatrix * position; position = gl_ModelViewProjectionMatrix * position;
normal = normalize(gl_ModelViewMatrix * normal); normal = normalize(gl_ModelViewMatrix * normal);
// pass along the texture coordinate // pass along the vertex color
gl_FrontColor = gl_Color;
// and the texture coordinates
gl_TexCoord[0] = gl_MultiTexCoord0; gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = position; gl_Position = position;

View file

@ -66,7 +66,8 @@ bool SkeletonModel::render(float alpha) {
glm::vec3 parentPosition; glm::vec3 parentPosition;
getJointPosition(parentIndex, parentPosition); getJointPosition(parentIndex, parentPosition);
const float STICK_RADIUS = BALL_RADIUS * 0.5f; const float STICK_RADIUS = BALL_RADIUS * 0.5f;
Avatar::renderJointConnectingCone(parentPosition, position, STICK_RADIUS, STICK_RADIUS); Avatar::renderJointConnectingCone(parentPosition, position, STICK_RADIUS * _owningAvatar->getScale(),
STICK_RADIUS * _owningAvatar->getScale());
} }
Model::render(alpha); Model::render(alpha);

View file

@ -9,6 +9,7 @@
#include <QBuffer> #include <QBuffer>
#include <QDataStream> #include <QDataStream>
#include <QIODevice> #include <QIODevice>
#include <QStringList>
#include <QTextStream> #include <QTextStream>
#include <QtDebug> #include <QtDebug>
#include <QtEndian> #include <QtEndian>
@ -17,6 +18,10 @@
#include <glm/gtx/quaternion.hpp> #include <glm/gtx/quaternion.hpp>
#include <glm/gtx/transform.hpp> #include <glm/gtx/transform.hpp>
#include <OctalCode.h>
#include <VoxelTree.h>
#include "FBXReader.h" #include "FBXReader.h"
#include "Util.h" #include "Util.h"
@ -397,6 +402,19 @@ glm::vec3 getVec3(const QVariantList& properties, int index) {
properties.at(index + 2).value<double>()); properties.at(index + 2).value<double>());
} }
glm::vec3 parseVec3(const QString& string) {
QStringList elements = string.split(',');
if (elements.isEmpty()) {
return glm::vec3();
}
glm::vec3 value;
for (int i = 0; i < 3; i++) {
// duplicate last value if there aren't three elements
value[i] = elements.at(min(i, elements.size() - 1)).trimmed().toFloat();
}
return value;
}
const char* FACESHIFT_BLENDSHAPES[] = { const char* FACESHIFT_BLENDSHAPES[] = {
"EyeBlink_L", "EyeBlink_L",
"EyeBlink_R", "EyeBlink_R",
@ -888,11 +906,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
foreach (const FBXNode& connection, child.children) { foreach (const FBXNode& connection, child.children) {
if (connection.name == "C" || connection.name == "Connect") { if (connection.name == "C" || connection.name == "Connect") {
if (connection.properties.at(0) == "OP") { if (connection.properties.at(0) == "OP") {
if (connection.properties.at(3) == "DiffuseColor") { QByteArray type = connection.properties.at(3).toByteArray().toLower();
if (type.contains("diffuse")) {
diffuseTextures.insert(connection.properties.at(2).toString(), diffuseTextures.insert(connection.properties.at(2).toString(),
connection.properties.at(1).toString()); connection.properties.at(1).toString());
} else if (connection.properties.at(3) == "Bump") { } else if (type.contains("bump")) {
bumpTextures.insert(connection.properties.at(2).toString(), bumpTextures.insert(connection.properties.at(2).toString(),
connection.properties.at(1).toString()); connection.properties.at(1).toString());
} }
@ -1021,6 +1040,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
QString diffuseTextureID = diffuseTextures.value(childID); QString diffuseTextureID = diffuseTextures.value(childID);
if (!diffuseTextureID.isNull()) { if (!diffuseTextureID.isNull()) {
part.diffuseFilename = textureFilenames.value(diffuseTextureID); part.diffuseFilename = textureFilenames.value(diffuseTextureID);
// FBX files generated by 3DSMax have an intermediate texture parent, apparently
foreach (const QString& childTextureID, childMap.values(diffuseTextureID)) {
if (textureFilenames.contains(childTextureID)) {
part.diffuseFilename = textureFilenames.value(childTextureID);
}
}
} }
QString bumpTextureID = bumpTextures.value(childID); QString bumpTextureID = bumpTextures.value(childID);
if (!bumpTextureID.isNull()) { if (!bumpTextureID.isNull()) {
@ -1129,6 +1155,34 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
geometry.meshes.append(mesh); geometry.meshes.append(mesh);
} }
// process attachments
QVariantHash attachments = mapping.value("attach").toHash();
for (QVariantHash::const_iterator it = attachments.constBegin(); it != attachments.constEnd(); it++) {
FBXAttachment attachment;
attachment.jointIndex = modelIDs.indexOf(it.key());
attachment.scale = glm::vec3(1.0f, 1.0f, 1.0f);
QVariantList properties = it->toList();
if (properties.isEmpty()) {
attachment.url = it->toUrl();
} else {
attachment.url = properties.at(0).toString();
if (properties.size() >= 2) {
attachment.translation = parseVec3(properties.at(1).toString());
if (properties.size() >= 3) {
attachment.rotation = glm::quat(glm::radians(parseVec3(properties.at(2).toString())));
if (properties.size() >= 4) {
attachment.scale = parseVec3(properties.at(3).toString());
}
}
}
}
geometry.attachments.append(attachment);
}
return geometry; return geometry;
} }
@ -1142,3 +1196,102 @@ FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping) {
return extractFBXGeometry(parseFBX(&modelBuffer), parseMapping(&mappingBuffer)); return extractFBXGeometry(parseFBX(&modelBuffer), parseMapping(&mappingBuffer));
} }
bool addMeshVoxelsOperation(VoxelNode* node, void* extraData) {
if (!node->isLeaf()) {
return true;
}
FBXMesh& mesh = *static_cast<FBXMesh*>(extraData);
FBXMeshPart& part = mesh.parts[0];
const int FACE_COUNT = 6;
const int VERTICES_PER_FACE = 4;
const int VERTEX_COUNT = FACE_COUNT * VERTICES_PER_FACE;
const float EIGHT_BIT_MAXIMUM = 255.0f;
glm::vec3 color = glm::vec3(node->getColor()[0], node->getColor()[1], node->getColor()[2]) / EIGHT_BIT_MAXIMUM;
for (int i = 0; i < VERTEX_COUNT; i++) {
part.quadIndices.append(part.quadIndices.size());
mesh.colors.append(color);
}
glm::vec3 corner = node->getCorner();
float scale = node->getScale();
mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z));
mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z + scale));
mesh.vertices.append(glm::vec3(corner.x, corner.y + scale, corner.z + scale));
mesh.vertices.append(glm::vec3(corner.x, corner.y + scale, corner.z));
for (int i = 0; i < VERTICES_PER_FACE; i++) {
mesh.normals.append(glm::vec3(-1.0f, 0.0f, 0.0f));
}
mesh.vertices.append(glm::vec3(corner.x + scale, corner.y, corner.z));
mesh.vertices.append(glm::vec3(corner.x + scale, corner.y + scale, corner.z));
mesh.vertices.append(glm::vec3(corner.x + scale, corner.y + scale, corner.z + scale));
mesh.vertices.append(glm::vec3(corner.x + scale, corner.y, corner.z + scale));
for (int i = 0; i < VERTICES_PER_FACE; i++) {
mesh.normals.append(glm::vec3(1.0f, 0.0f, 0.0f));
}
mesh.vertices.append(glm::vec3(corner.x + scale, corner.y, corner.z));
mesh.vertices.append(glm::vec3(corner.x + scale, corner.y, corner.z + scale));
mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z + scale));
mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z));
for (int i = 0; i < VERTICES_PER_FACE; i++) {
mesh.normals.append(glm::vec3(0.0f, -1.0f, 0.0f));
}
mesh.vertices.append(glm::vec3(corner.x, corner.y + scale, corner.z));
mesh.vertices.append(glm::vec3(corner.x, corner.y + scale, corner.z + scale));
mesh.vertices.append(glm::vec3(corner.x + scale, corner.y + scale, corner.z + scale));
mesh.vertices.append(glm::vec3(corner.x + scale, corner.y + scale, corner.z));
for (int i = 0; i < VERTICES_PER_FACE; i++) {
mesh.normals.append(glm::vec3(0.0f, 1.0f, 0.0f));
}
mesh.vertices.append(glm::vec3(corner.x, corner.y + scale, corner.z));
mesh.vertices.append(glm::vec3(corner.x + scale, corner.y + scale, corner.z));
mesh.vertices.append(glm::vec3(corner.x + scale, corner.y, corner.z));
mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z));
for (int i = 0; i < VERTICES_PER_FACE; i++) {
mesh.normals.append(glm::vec3(0.0f, 0.0f, -1.0f));
}
mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z + scale));
mesh.vertices.append(glm::vec3(corner.x + scale, corner.y, corner.z + scale));
mesh.vertices.append(glm::vec3(corner.x + scale, corner.y + scale, corner.z + scale));
mesh.vertices.append(glm::vec3(corner.x, corner.y + scale, corner.z + scale));
for (int i = 0; i < VERTICES_PER_FACE; i++) {
mesh.normals.append(glm::vec3(0.0f, 0.0f, 1.0f));
}
return true;
}
FBXGeometry readSVO(const QByteArray& model) {
FBXGeometry geometry;
// we have one joint
FBXJoint joint = { -1 };
geometry.joints.append(joint);
// and one mesh with one cluster and one part
FBXMesh mesh;
mesh.isEye = false;
mesh.springiness = 0.0f;
FBXCluster cluster = { 0 };
mesh.clusters.append(cluster);
FBXMeshPart part;
part.diffuseColor = glm::vec3(1.0f, 1.0f, 1.0f);
part.shininess = 96.0f;
mesh.parts.append(part);
VoxelTree tree;
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS);
tree.readBitstreamToTree((unsigned char*)model.data(), model.size(), args);
tree.recurseTreeWithOperation(addMeshVoxelsOperation, &mesh);
geometry.meshes.append(mesh);
return geometry;
}

View file

@ -9,6 +9,7 @@
#ifndef __interface__FBXReader__ #ifndef __interface__FBXReader__
#define __interface__FBXReader__ #define __interface__FBXReader__
#include <QUrl>
#include <QVarLengthArray> #include <QVarLengthArray>
#include <QVariant> #include <QVariant>
#include <QVector> #include <QVector>
@ -83,6 +84,7 @@ public:
QVector<glm::vec3> vertices; QVector<glm::vec3> vertices;
QVector<glm::vec3> normals; QVector<glm::vec3> normals;
QVector<glm::vec3> colors;
QVector<glm::vec2> texCoords; QVector<glm::vec2> texCoords;
QVector<glm::vec4> clusterIndices; QVector<glm::vec4> clusterIndices;
QVector<glm::vec4> clusterWeights; QVector<glm::vec4> clusterWeights;
@ -98,6 +100,17 @@ public:
QVector<QVarLengthArray<QPair<int, int>, 4> > vertexConnections; QVector<QVarLengthArray<QPair<int, int>, 4> > vertexConnections;
}; };
/// An attachment to an FBX document.
class FBXAttachment {
public:
int jointIndex;
QUrl url;
glm::vec3 translation;
glm::quat rotation;
glm::vec3 scale;
};
/// A set of meshes extracted from an FBX document. /// A set of meshes extracted from an FBX document.
class FBXGeometry { class FBXGeometry {
public: public:
@ -117,10 +130,15 @@ public:
int headJointIndex; int headJointIndex;
glm::vec3 neckPivot; glm::vec3 neckPivot;
QVector<FBXAttachment> attachments;
}; };
/// Reads FBX geometry from the supplied model and mapping data. /// Reads FBX geometry from the supplied model and mapping data.
/// \exception QString if an error occurs in parsing /// \exception QString if an error occurs in parsing
FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping); FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping);
/// Reads SVO geometry from the supplied model data.
FBXGeometry readSVO(const QByteArray& model);
#endif /* defined(__interface__FBXReader__) */ #endif /* defined(__interface__FBXReader__) */

View file

@ -348,7 +348,7 @@ void NetworkGeometry::maybeReadModelWithMapping() {
} }
try { try {
_geometry = readFBX(model, mapping); _geometry = url.path().toLower().endsWith(".svo") ? readSVO(model) : readFBX(model, mapping);
} catch (const QString& error) { } catch (const QString& error) {
qDebug() << "Error reading " << url << ": " << error << "\n"; qDebug() << "Error reading " << url << ": " << error << "\n";
@ -395,35 +395,43 @@ 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) {
glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3) + int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3);
mesh.texCoords.size() * sizeof(glm::vec2) + (mesh.clusterIndices.size() + int colorsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3);
mesh.clusterWeights.size()) * sizeof(glm::vec4), NULL, GL_STATIC_DRAW); 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.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, mesh.vertices.size() * sizeof(glm::vec3), glBufferSubData(GL_ARRAY_BUFFER, normalsOffset, mesh.normals.size() * sizeof(glm::vec3), mesh.normals.constData());
mesh.normals.size() * sizeof(glm::vec3), mesh.normals.constData()); glBufferSubData(GL_ARRAY_BUFFER, colorsOffset, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData());
glBufferSubData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3), glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2),
mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData()); mesh.texCoords.constData());
glBufferSubData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3) + glBufferSubData(GL_ARRAY_BUFFER, clusterIndicesOffset, mesh.clusterIndices.size() * sizeof(glm::vec4),
mesh.texCoords.size() * sizeof(glm::vec2), mesh.clusterIndices.size() * sizeof(glm::vec4),
mesh.clusterIndices.constData()); mesh.clusterIndices.constData());
glBufferSubData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3) + glBufferSubData(GL_ARRAY_BUFFER, clusterWeightsOffset, mesh.clusterWeights.size() * sizeof(glm::vec4),
mesh.texCoords.size() * sizeof(glm::vec2) + mesh.clusterIndices.size() * sizeof(glm::vec4), mesh.clusterWeights.constData());
mesh.clusterWeights.size() * sizeof(glm::vec4), mesh.clusterWeights.constData());
// 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) {
glBufferData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2) + (mesh.clusterIndices.size() + int texCoordsOffset = mesh.colors.size() * sizeof(glm::vec3);
mesh.clusterWeights.size()) * sizeof(glm::vec4), NULL, GL_STATIC_DRAW); int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2);
glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData()); int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4);
glBufferSubData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2), glBufferData(GL_ARRAY_BUFFER, clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4),
mesh.clusterIndices.size() * sizeof(glm::vec4), mesh.clusterIndices.constData()); NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2) + glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData());
mesh.clusterIndices.size() * sizeof(glm::vec4), mesh.clusterWeights.size() * sizeof(glm::vec4), 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());
glBufferSubData(GL_ARRAY_BUFFER, clusterWeightsOffset, mesh.clusterWeights.size() * sizeof(glm::vec4),
mesh.clusterWeights.constData()); mesh.clusterWeights.constData());
} else { } else {
glBufferData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2), NULL, GL_STATIC_DRAW); int texCoordsOffset = mesh.colors.size() * sizeof(glm::vec3);
glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData()); 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, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2),
mesh.texCoords.constData());
} }
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);

View file

@ -13,7 +13,8 @@
using namespace std; using namespace std;
Model::Model() : Model::Model(QObject* parent) :
QObject(parent),
_pupilDilation(0.0f) _pupilDilation(0.0f)
{ {
// we may have been created in the network thread, but we live in the main thread // we may have been created in the network thread, but we live in the main thread
@ -56,6 +57,10 @@ void Model::init() {
void Model::reset() { void Model::reset() {
_resetStates = true; _resetStates = true;
foreach (Model* attachment, _attachments) {
attachment->reset();
}
} }
void Model::simulate(float deltaTime) { void Model::simulate(float deltaTime) {
@ -81,6 +86,12 @@ void Model::simulate(float deltaTime) {
} }
_meshStates.append(state); _meshStates.append(state);
} }
foreach (const FBXAttachment& attachment, geometry.attachments) {
Model* model = new Model(this);
model->init();
model->setURL(attachment.url);
_attachments.append(model);
}
_resetStates = true; _resetStates = true;
} }
@ -89,6 +100,23 @@ void Model::simulate(float deltaTime) {
updateJointState(i); updateJointState(i);
} }
// update the attachment transforms and simulate them
for (int i = 0; i < _attachments.size(); i++) {
const FBXAttachment& attachment = geometry.attachments.at(i);
Model* model = _attachments.at(i);
glm::vec3 jointTranslation = _translation;
glm::quat jointRotation = _rotation;
getJointPosition(attachment.jointIndex, jointTranslation);
getJointRotation(attachment.jointIndex, jointRotation);
model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale);
model->setRotation(jointRotation * attachment.rotation);
model->setScale(_scale * attachment.scale);
model->simulate(deltaTime);
}
for (int i = 0; i < _meshStates.size(); i++) { for (int i = 0; i < _meshStates.size(); i++) {
MeshState& state = _meshStates[i]; MeshState& state = _meshStates[i];
const FBXMesh& mesh = geometry.meshes.at(i); const FBXMesh& mesh = geometry.meshes.at(i);
@ -175,6 +203,10 @@ void Model::simulate(float deltaTime) {
} }
bool Model::render(float alpha) { bool Model::render(float alpha) {
// render the attachments
foreach (Model* attachment, _attachments) {
attachment->render(alpha);
}
if (_meshStates.isEmpty()) { if (_meshStates.isEmpty()) {
return false; return false;
} }
@ -202,7 +234,6 @@ bool Model::render(float alpha) {
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_COLOR_MATERIAL); glDisable(GL_COLOR_MATERIAL);
@ -221,8 +252,8 @@ bool Model::render(float alpha) {
_skinProgram.bind(); _skinProgram.bind();
glUniformMatrix4fvARB(_clusterMatricesLocation, state.clusterMatrices.size(), false, glUniformMatrix4fvARB(_clusterMatricesLocation, state.clusterMatrices.size(), false,
(const float*)state.clusterMatrices.constData()); (const float*)state.clusterMatrices.constData());
int offset = vertexCount * sizeof(glm::vec2) + (mesh.blendshapes.isEmpty() ? int offset = mesh.colors.size() * sizeof(glm::vec3) + mesh.texCoords.size() * sizeof(glm::vec2) +
vertexCount * 2 * sizeof(glm::vec3) : 0); (mesh.blendshapes.isEmpty() ? vertexCount * 2 * sizeof(glm::vec3) : 0);
_skinProgram.setAttributeBuffer(_clusterIndicesLocation, GL_FLOAT, offset, 4); _skinProgram.setAttributeBuffer(_clusterIndicesLocation, GL_FLOAT, offset, 4);
_skinProgram.setAttributeBuffer(_clusterWeightsLocation, GL_FLOAT, _skinProgram.setAttributeBuffer(_clusterWeightsLocation, GL_FLOAT,
offset + vertexCount * sizeof(glm::vec4), 4); offset + vertexCount * sizeof(glm::vec4), 4);
@ -239,10 +270,13 @@ bool Model::render(float alpha) {
} }
if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) {
glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3))); glColorPointer(3, 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)));
} else { } else {
glTexCoordPointer(2, GL_FLOAT, 0, 0); glColorPointer(3, GL_FLOAT, 0, 0);
glTexCoordPointer(2, GL_FLOAT, 0, (void*)(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()) {
@ -281,6 +315,15 @@ bool Model::render(float alpha) {
glVertexPointer(3, GL_FLOAT, 0, 0); glVertexPointer(3, GL_FLOAT, 0, 0);
glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3))); glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3)));
if (!mesh.colors.isEmpty()) {
glEnableClientState(GL_COLOR_ARRAY);
} else {
glColor3f(1.0f, 1.0f, 1.0f);
}
if (!mesh.texCoords.isEmpty()) {
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
qint64 offset = 0; qint64 offset = 0;
for (int j = 0; j < networkMesh.parts.size(); j++) { for (int j = 0; j < networkMesh.parts.size(); j++) {
const NetworkMeshPart& networkPart = networkMesh.parts.at(j); const NetworkMeshPart& networkPart = networkMesh.parts.at(j);
@ -311,6 +354,13 @@ bool Model::render(float alpha) {
offset += part.triangleIndices.size() * sizeof(int); offset += part.triangleIndices.size() * sizeof(int);
} }
if (!mesh.colors.isEmpty()) {
glDisableClientState(GL_COLOR_ARRAY);
}
if (!mesh.texCoords.isEmpty()) {
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
if (state.worldSpaceVertices.isEmpty()) { if (state.worldSpaceVertices.isEmpty()) {
if (state.clusterMatrices.size() > 1) { if (state.clusterMatrices.size() > 1) {
_skinProgram.disableAttributeArray(_clusterIndicesLocation); _skinProgram.disableAttributeArray(_clusterIndicesLocation);
@ -443,6 +493,10 @@ bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const {
} }
void Model::deleteGeometry() { void Model::deleteGeometry() {
foreach (Model* attachment, _attachments) {
delete attachment;
}
_attachments.clear();
foreach (GLuint id, _blendedVertexBufferIDs) { foreach (GLuint id, _blendedVertexBufferIDs) {
glDeleteBuffers(1, &id); glDeleteBuffers(1, &id);
} }

View file

@ -23,7 +23,7 @@ class Model : public QObject {
public: public:
Model(); Model(QObject* parent = NULL);
virtual ~Model(); virtual ~Model();
void setTranslation(const glm::vec3& translation) { _translation = translation; } void setTranslation(const glm::vec3& translation) { _translation = translation; }
@ -126,6 +126,8 @@ private:
QVector<glm::vec3> _blendedVertices; QVector<glm::vec3> _blendedVertices;
QVector<glm::vec3> _blendedNormals; QVector<glm::vec3> _blendedNormals;
QVector<Model*> _attachments;
static ProgramObject _program; static ProgramObject _program;
static ProgramObject _skinProgram; static ProgramObject _skinProgram;
static int _clusterMatricesLocation; static int _clusterMatricesLocation;