mirror of
https://github.com/overte-org/overte.git
synced 2025-08-04 10:28:22 +02:00
Merge branch 'master' of https://github.com/worklist/hifi
Conflicts: libraries/avatars/src/AvatarData.h libraries/voxel-server-library/src/VoxelSendThread.cpp
This commit is contained in:
commit
d341c09d3d
14 changed files with 523 additions and 50 deletions
|
@ -17,8 +17,8 @@ varying vec4 normal;
|
|||
void main(void) {
|
||||
// compute the base color based on OpenGL lighting model
|
||||
vec4 normalizedNormal = normalize(normal);
|
||||
vec4 base = gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
|
||||
gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[0].position));
|
||||
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
|
||||
gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalizedNormal, 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)), normalizedNormal));
|
||||
|
|
|
@ -16,7 +16,10 @@ void main(void) {
|
|||
// transform and store the normal for interpolation
|
||||
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;
|
||||
|
||||
// use standard pipeline transform
|
||||
|
|
|
@ -31,7 +31,10 @@ void main(void) {
|
|||
position = gl_ModelViewProjectionMatrix * position;
|
||||
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_Position = position;
|
||||
|
|
|
@ -66,7 +66,8 @@ bool SkeletonModel::render(float alpha) {
|
|||
glm::vec3 parentPosition;
|
||||
getJointPosition(parentIndex, parentPosition);
|
||||
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);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <QBuffer>
|
||||
#include <QDataStream>
|
||||
#include <QIODevice>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
#include <QtDebug>
|
||||
#include <QtEndian>
|
||||
|
@ -17,6 +18,10 @@
|
|||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <OctalCode.h>
|
||||
|
||||
#include <VoxelTree.h>
|
||||
|
||||
#include "FBXReader.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
@ -397,6 +402,19 @@ glm::vec3 getVec3(const QVariantList& properties, int index) {
|
|||
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[] = {
|
||||
"EyeBlink_L",
|
||||
"EyeBlink_R",
|
||||
|
@ -888,11 +906,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
foreach (const FBXNode& connection, child.children) {
|
||||
if (connection.name == "C" || connection.name == "Connect") {
|
||||
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(),
|
||||
connection.properties.at(1).toString());
|
||||
|
||||
} else if (connection.properties.at(3) == "Bump") {
|
||||
} else if (type.contains("bump")) {
|
||||
bumpTextures.insert(connection.properties.at(2).toString(),
|
||||
connection.properties.at(1).toString());
|
||||
}
|
||||
|
@ -1021,6 +1040,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
QString diffuseTextureID = diffuseTextures.value(childID);
|
||||
if (!diffuseTextureID.isNull()) {
|
||||
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);
|
||||
if (!bumpTextureID.isNull()) {
|
||||
|
@ -1129,6 +1155,34 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -1142,3 +1196,102 @@ FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping) {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#ifndef __interface__FBXReader__
|
||||
#define __interface__FBXReader__
|
||||
|
||||
#include <QUrl>
|
||||
#include <QVarLengthArray>
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
@ -83,6 +84,7 @@ public:
|
|||
|
||||
QVector<glm::vec3> vertices;
|
||||
QVector<glm::vec3> normals;
|
||||
QVector<glm::vec3> colors;
|
||||
QVector<glm::vec2> texCoords;
|
||||
QVector<glm::vec4> clusterIndices;
|
||||
QVector<glm::vec4> clusterWeights;
|
||||
|
@ -98,6 +100,17 @@ public:
|
|||
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.
|
||||
class FBXGeometry {
|
||||
public:
|
||||
|
@ -117,10 +130,15 @@ public:
|
|||
int headJointIndex;
|
||||
|
||||
glm::vec3 neckPivot;
|
||||
|
||||
QVector<FBXAttachment> attachments;
|
||||
};
|
||||
|
||||
/// Reads FBX geometry from the supplied model and mapping data.
|
||||
/// \exception QString if an error occurs in parsing
|
||||
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__) */
|
||||
|
|
|
@ -348,7 +348,7 @@ void NetworkGeometry::maybeReadModelWithMapping() {
|
|||
}
|
||||
|
||||
try {
|
||||
_geometry = readFBX(model, mapping);
|
||||
_geometry = url.path().toLower().endsWith(".svo") ? readSVO(model) : readFBX(model, mapping);
|
||||
|
||||
} catch (const QString& error) {
|
||||
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 (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) {
|
||||
glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3) +
|
||||
mesh.texCoords.size() * sizeof(glm::vec2) + (mesh.clusterIndices.size() +
|
||||
mesh.clusterWeights.size()) * sizeof(glm::vec4), NULL, GL_STATIC_DRAW);
|
||||
int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3);
|
||||
int colorsOffset = normalsOffset + mesh.normals.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.vertices.size() * sizeof(glm::vec3), mesh.vertices.constData());
|
||||
glBufferSubData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(glm::vec3),
|
||||
mesh.normals.size() * sizeof(glm::vec3), mesh.normals.constData());
|
||||
glBufferSubData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3),
|
||||
mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData());
|
||||
glBufferSubData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3) +
|
||||
mesh.texCoords.size() * sizeof(glm::vec2), mesh.clusterIndices.size() * sizeof(glm::vec4),
|
||||
glBufferSubData(GL_ARRAY_BUFFER, normalsOffset, 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, 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, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3) +
|
||||
mesh.texCoords.size() * sizeof(glm::vec2) + mesh.clusterIndices.size() * sizeof(glm::vec4),
|
||||
mesh.clusterWeights.size() * sizeof(glm::vec4), mesh.clusterWeights.constData());
|
||||
glBufferSubData(GL_ARRAY_BUFFER, clusterWeightsOffset, mesh.clusterWeights.size() * sizeof(glm::vec4),
|
||||
mesh.clusterWeights.constData());
|
||||
|
||||
// if there's no springiness, then the cluster indices/weights can be static
|
||||
} else if (mesh.springiness == 0.0f) {
|
||||
glBufferData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2) + (mesh.clusterIndices.size() +
|
||||
mesh.clusterWeights.size()) * sizeof(glm::vec4), NULL, GL_STATIC_DRAW);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData());
|
||||
glBufferSubData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2),
|
||||
mesh.clusterIndices.size() * sizeof(glm::vec4), mesh.clusterIndices.constData());
|
||||
glBufferSubData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2) +
|
||||
mesh.clusterIndices.size() * sizeof(glm::vec4), mesh.clusterWeights.size() * sizeof(glm::vec4),
|
||||
int texCoordsOffset = 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, 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());
|
||||
|
||||
} else {
|
||||
glBufferData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2), NULL, GL_STATIC_DRAW);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData());
|
||||
int texCoordsOffset = 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, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2),
|
||||
mesh.texCoords.constData());
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
Model::Model() :
|
||||
Model::Model(QObject* parent) :
|
||||
QObject(parent),
|
||||
_pupilDilation(0.0f)
|
||||
{
|
||||
// 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() {
|
||||
_resetStates = true;
|
||||
|
||||
foreach (Model* attachment, _attachments) {
|
||||
attachment->reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Model::simulate(float deltaTime) {
|
||||
|
@ -81,6 +86,12 @@ void Model::simulate(float deltaTime) {
|
|||
}
|
||||
_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;
|
||||
}
|
||||
|
||||
|
@ -89,6 +100,23 @@ void Model::simulate(float deltaTime) {
|
|||
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++) {
|
||||
MeshState& state = _meshStates[i];
|
||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||
|
@ -175,6 +203,10 @@ void Model::simulate(float deltaTime) {
|
|||
}
|
||||
|
||||
bool Model::render(float alpha) {
|
||||
// render the attachments
|
||||
foreach (Model* attachment, _attachments) {
|
||||
attachment->render(alpha);
|
||||
}
|
||||
if (_meshStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -202,7 +234,6 @@ bool Model::render(float alpha) {
|
|||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
glDisable(GL_COLOR_MATERIAL);
|
||||
|
||||
|
@ -221,8 +252,8 @@ bool Model::render(float alpha) {
|
|||
_skinProgram.bind();
|
||||
glUniformMatrix4fvARB(_clusterMatricesLocation, state.clusterMatrices.size(), false,
|
||||
(const float*)state.clusterMatrices.constData());
|
||||
int offset = vertexCount * sizeof(glm::vec2) + (mesh.blendshapes.isEmpty() ?
|
||||
vertexCount * 2 * sizeof(glm::vec3) : 0);
|
||||
int offset = 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,
|
||||
offset + vertexCount * sizeof(glm::vec4), 4);
|
||||
|
@ -239,10 +270,13 @@ bool Model::render(float alpha) {
|
|||
}
|
||||
|
||||
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 {
|
||||
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));
|
||||
|
||||
if (!state.worldSpaceVertices.isEmpty()) {
|
||||
|
@ -281,6 +315,15 @@ bool Model::render(float alpha) {
|
|||
glVertexPointer(3, GL_FLOAT, 0, 0);
|
||||
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;
|
||||
for (int j = 0; j < networkMesh.parts.size(); j++) {
|
||||
const NetworkMeshPart& networkPart = networkMesh.parts.at(j);
|
||||
|
@ -311,6 +354,13 @@ bool Model::render(float alpha) {
|
|||
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.clusterMatrices.size() > 1) {
|
||||
_skinProgram.disableAttributeArray(_clusterIndicesLocation);
|
||||
|
@ -443,6 +493,10 @@ bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const {
|
|||
}
|
||||
|
||||
void Model::deleteGeometry() {
|
||||
foreach (Model* attachment, _attachments) {
|
||||
delete attachment;
|
||||
}
|
||||
_attachments.clear();
|
||||
foreach (GLuint id, _blendedVertexBufferIDs) {
|
||||
glDeleteBuffers(1, &id);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ class Model : public QObject {
|
|||
|
||||
public:
|
||||
|
||||
Model();
|
||||
Model(QObject* parent = NULL);
|
||||
virtual ~Model();
|
||||
|
||||
void setTranslation(const glm::vec3& translation) { _translation = translation; }
|
||||
|
@ -126,6 +126,8 @@ private:
|
|||
QVector<glm::vec3> _blendedVertices;
|
||||
QVector<glm::vec3> _blendedNormals;
|
||||
|
||||
QVector<Model*> _attachments;
|
||||
|
||||
static ProgramObject _program;
|
||||
static ProgramObject _skinProgram;
|
||||
static int _clusterMatricesLocation;
|
||||
|
|
|
@ -118,11 +118,8 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
|
|||
setSemiNibbleAt(bitItems,KEY_STATE_START_BIT,_keyState);
|
||||
// hand state
|
||||
setSemiNibbleAt(bitItems,HAND_STATE_START_BIT,_handState);
|
||||
*destinationBuffer++ = bitItems;
|
||||
|
||||
bitItems = 0;
|
||||
// faceshift state
|
||||
if (_headData->_isFaceshiftConnected) { setAtBit(bitItems, IS_FACESHIFT_CONNECTED); }
|
||||
|
||||
*destinationBuffer++ = bitItems;
|
||||
|
||||
// If it is connected, pack up the data
|
||||
|
@ -249,7 +246,6 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
// hand state, stored as a semi-nibble in the bitItems
|
||||
_handState = getSemiNibbleAt(bitItems,HAND_STATE_START_BIT);
|
||||
|
||||
bitItems = (unsigned char)*sourceBuffer++;
|
||||
_headData->_isFaceshiftConnected = oneAtBit(bitItems, IS_FACESHIFT_CONNECTED);
|
||||
|
||||
// If it is connected, pack up the data
|
||||
|
|
|
@ -27,11 +27,9 @@
|
|||
#include "HandData.h"
|
||||
|
||||
// First bitset
|
||||
const int KEY_STATE_START_BIT = 3; // 4th and 5th bits
|
||||
const int HAND_STATE_START_BIT = 5; // 6th and 7th bits
|
||||
|
||||
// Second bitset
|
||||
const int IS_FACESHIFT_CONNECTED = 0;
|
||||
const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits
|
||||
const int HAND_STATE_START_BIT = 2; // 3rd and 4th bits
|
||||
const int IS_FACESHIFT_CONNECTED = 4; // 5th bit
|
||||
|
||||
const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation
|
||||
|
||||
|
|
|
@ -223,10 +223,11 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
|
|||
int clientMaxPacketsPerInterval = nodeData->getMaxVoxelPacketsPerSecond() / INTERVALS_PER_SECOND;
|
||||
int maxPacketsPerInterval = std::max(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
|
||||
|
||||
|
||||
printf("deepestLevelVoxelDistributor()... packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
|
||||
packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
|
||||
nodeData->getMaxVoxelPacketsPerSecond(), clientMaxPacketsPerInterval);
|
||||
if (_myServer->wantsDebugVoxelSending()) {
|
||||
printf("packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
|
||||
packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
|
||||
nodeData->getMaxVoxelPacketsPerSecond(), clientMaxPacketsPerInterval);
|
||||
}
|
||||
|
||||
while (packetsSentThisInterval < maxPacketsPerInterval - (shouldSendEnvironments ? 1 : 0)) {
|
||||
// Check to see if we're taking too long, and if so bail early...
|
||||
|
|
131
libraries/voxels/src/VoxelQuery.cpp
Normal file
131
libraries/voxels/src/VoxelQuery.cpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
//
|
||||
// VoxelQuery.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 10/24/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
#include "VoxelConstants.h"
|
||||
|
||||
#include "VoxelQuery.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const float fingerVectorRadix = 4; // bits of precision when converting from float<->fixed
|
||||
|
||||
VoxelQuery::VoxelQuery(Node* owningNode) :
|
||||
NodeData(owningNode),
|
||||
_uuid(),
|
||||
_cameraPosition(0,0,0),
|
||||
_cameraOrientation(),
|
||||
_cameraFov(0.0f),
|
||||
_cameraAspectRatio(0.0f),
|
||||
_cameraNearClip(0.0f),
|
||||
_cameraFarClip(0.0f),
|
||||
_wantColor(true),
|
||||
_wantDelta(true),
|
||||
_wantLowResMoving(true),
|
||||
_wantOcclusionCulling(true),
|
||||
_maxVoxelPPS(DEFAULT_MAX_VOXEL_PPS)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
VoxelQuery::~VoxelQuery() {
|
||||
}
|
||||
|
||||
int VoxelQuery::getBroadcastData(unsigned char* destinationBuffer) {
|
||||
unsigned char* bufferStart = destinationBuffer;
|
||||
|
||||
// TODO: DRY this up to a shared method
|
||||
// that can pack any type given the number of bytes
|
||||
// and return the number of bytes to push the pointer
|
||||
|
||||
// UUID
|
||||
QByteArray uuidByteArray = _uuid.toRfc4122();
|
||||
memcpy(destinationBuffer, uuidByteArray.constData(), uuidByteArray.size());
|
||||
destinationBuffer += uuidByteArray.size();
|
||||
|
||||
// camera details
|
||||
memcpy(destinationBuffer, &_cameraPosition, sizeof(_cameraPosition));
|
||||
destinationBuffer += sizeof(_cameraPosition);
|
||||
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _cameraOrientation);
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _cameraFov);
|
||||
destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _cameraAspectRatio);
|
||||
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraNearClip);
|
||||
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraFarClip);
|
||||
memcpy(destinationBuffer, &_cameraEyeOffsetPosition, sizeof(_cameraEyeOffsetPosition));
|
||||
destinationBuffer += sizeof(_cameraEyeOffsetPosition);
|
||||
|
||||
// bitMask of less than byte wide items
|
||||
unsigned char bitItems = 0;
|
||||
if (_wantLowResMoving) { setAtBit(bitItems, WANT_LOW_RES_MOVING_BIT); }
|
||||
if (_wantColor) { setAtBit(bitItems, WANT_COLOR_AT_BIT); }
|
||||
if (_wantDelta) { setAtBit(bitItems, WANT_DELTA_AT_BIT); }
|
||||
if (_wantOcclusionCulling) { setAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT); }
|
||||
|
||||
*destinationBuffer++ = bitItems;
|
||||
|
||||
// desired Max Voxel PPS
|
||||
memcpy(destinationBuffer, &_maxVoxelPPS, sizeof(_maxVoxelPPS));
|
||||
destinationBuffer += sizeof(_maxVoxelPPS);
|
||||
|
||||
return destinationBuffer - bufferStart;
|
||||
}
|
||||
|
||||
// called on the other nodes - assigns it to my views of the others
|
||||
int VoxelQuery::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||
|
||||
// increment to push past the packet header
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
|
||||
sourceBuffer += numBytesPacketHeader;
|
||||
|
||||
unsigned char* startPosition = sourceBuffer;
|
||||
|
||||
// push past the node session UUID
|
||||
sourceBuffer += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
// user UUID
|
||||
_uuid = QUuid::fromRfc4122(QByteArray((char*) sourceBuffer, NUM_BYTES_RFC4122_UUID));
|
||||
sourceBuffer += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
// camera details
|
||||
memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition));
|
||||
sourceBuffer += sizeof(_cameraPosition);
|
||||
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, _cameraOrientation);
|
||||
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_cameraFov);
|
||||
sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer,_cameraAspectRatio);
|
||||
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraNearClip);
|
||||
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraFarClip);
|
||||
memcpy(&_cameraEyeOffsetPosition, sourceBuffer, sizeof(_cameraEyeOffsetPosition));
|
||||
sourceBuffer += sizeof(_cameraEyeOffsetPosition);
|
||||
|
||||
// voxel sending features...
|
||||
unsigned char bitItems = 0;
|
||||
bitItems = (unsigned char)*sourceBuffer++;
|
||||
_wantLowResMoving = oneAtBit(bitItems, WANT_LOW_RES_MOVING_BIT);
|
||||
_wantColor = oneAtBit(bitItems, WANT_COLOR_AT_BIT);
|
||||
_wantDelta = oneAtBit(bitItems, WANT_DELTA_AT_BIT);
|
||||
_wantOcclusionCulling = oneAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT);
|
||||
|
||||
// desired Max Voxel PPS
|
||||
memcpy(&_maxVoxelPPS, sourceBuffer, sizeof(_maxVoxelPPS));
|
||||
sourceBuffer += sizeof(_maxVoxelPPS);
|
||||
|
||||
return sourceBuffer - startPosition;
|
||||
}
|
||||
|
||||
glm::vec3 VoxelQuery::calculateCameraDirection() const {
|
||||
glm::vec3 direction = glm::vec3(_cameraOrientation * glm::vec4(IDENTITY_FRONT, 0.0f));
|
||||
return direction;
|
||||
}
|
||||
|
105
libraries/voxels/src/VoxelQuery.h
Normal file
105
libraries/voxels/src/VoxelQuery.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
//
|
||||
// VoxelQuery.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 4/9/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__VoxelQuery__
|
||||
#define __hifi__VoxelQuery__
|
||||
|
||||
#include <string>
|
||||
#include <inttypes.h>
|
||||
#include <vector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QtCore/QVariantMap>
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include <NodeData.h>
|
||||
|
||||
// First bitset
|
||||
const int WANT_LOW_RES_MOVING_BIT = 0;
|
||||
const int WANT_COLOR_AT_BIT = 1;
|
||||
const int WANT_DELTA_AT_BIT = 2;
|
||||
const int WANT_OCCLUSION_CULLING_BIT = 3; // 4th bit
|
||||
|
||||
class VoxelQuery : public NodeData {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
VoxelQuery(Node* owningNode = NULL);
|
||||
~VoxelQuery();
|
||||
|
||||
int getBroadcastData(unsigned char* destinationBuffer);
|
||||
int parseData(unsigned char* sourceBuffer, int numBytes);
|
||||
|
||||
QUuid& getUUID() { return _uuid; }
|
||||
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
||||
|
||||
// getters for camera details
|
||||
const glm::vec3& getCameraPosition() const { return _cameraPosition; }
|
||||
const glm::quat& getCameraOrientation() const { return _cameraOrientation; }
|
||||
float getCameraFov() const { return _cameraFov; }
|
||||
float getCameraAspectRatio() const { return _cameraAspectRatio; }
|
||||
float getCameraNearClip() const { return _cameraNearClip; }
|
||||
float getCameraFarClip() const { return _cameraFarClip; }
|
||||
const glm::vec3& getCameraEyeOffsetPosition() const { return _cameraEyeOffsetPosition; }
|
||||
|
||||
glm::vec3 calculateCameraDirection() const;
|
||||
|
||||
// setters for camera details
|
||||
void setCameraPosition(const glm::vec3& position) { _cameraPosition = position; }
|
||||
void setCameraOrientation(const glm::quat& orientation) { _cameraOrientation = orientation; }
|
||||
void setCameraFov(float fov) { _cameraFov = fov; }
|
||||
void setCameraAspectRatio(float aspectRatio) { _cameraAspectRatio = aspectRatio; }
|
||||
void setCameraNearClip(float nearClip) { _cameraNearClip = nearClip; }
|
||||
void setCameraFarClip(float farClip) { _cameraFarClip = farClip; }
|
||||
void setCameraEyeOffsetPosition(const glm::vec3& eyeOffsetPosition) { _cameraEyeOffsetPosition = eyeOffsetPosition; }
|
||||
|
||||
// related to Voxel Sending strategies
|
||||
bool getWantColor() const { return _wantColor; }
|
||||
bool getWantDelta() const { return _wantDelta; }
|
||||
bool getWantLowResMoving() const { return _wantLowResMoving; }
|
||||
bool getWantOcclusionCulling() const { return _wantOcclusionCulling; }
|
||||
int getMaxVoxelPacketsPerSecond() const { return _maxVoxelPPS; }
|
||||
|
||||
public slots:
|
||||
void setWantLowResMoving(bool wantLowResMoving) { _wantLowResMoving = wantLowResMoving; }
|
||||
void setWantColor(bool wantColor) { _wantColor = wantColor; }
|
||||
void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; }
|
||||
void setWantOcclusionCulling(bool wantOcclusionCulling) { _wantOcclusionCulling = wantOcclusionCulling; }
|
||||
void setMaxVoxelPacketsPerSecond(int maxVoxelPPS) { _maxVoxelPPS = maxVoxelPPS; }
|
||||
|
||||
protected:
|
||||
QUuid _uuid;
|
||||
|
||||
// camera details for the avatar
|
||||
glm::vec3 _cameraPosition;
|
||||
glm::quat _cameraOrientation;
|
||||
float _cameraFov;
|
||||
float _cameraAspectRatio;
|
||||
float _cameraNearClip;
|
||||
float _cameraFarClip;
|
||||
glm::vec3 _cameraEyeOffsetPosition;
|
||||
|
||||
// voxel server sending items
|
||||
bool _wantColor;
|
||||
bool _wantDelta;
|
||||
bool _wantLowResMoving;
|
||||
bool _wantOcclusionCulling;
|
||||
int _maxVoxelPPS;
|
||||
|
||||
private:
|
||||
// privatize the copy constructor and assignment operator so they cannot be called
|
||||
VoxelQuery(const VoxelQuery&);
|
||||
VoxelQuery& operator= (const VoxelQuery&);
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__VoxelQuery__) */
|
Loading…
Reference in a new issue