mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 23:13:48 +02:00
Merge branch 'master' of github.com:worklist/hifi into assignment
This commit is contained in:
commit
e48a269fe3
20 changed files with 625 additions and 314 deletions
22
README.md
22
README.md
|
@ -64,8 +64,8 @@ development.
|
|||
Running Interface
|
||||
-----
|
||||
|
||||
Using finder locate the interface.app Application in build/interface/Debug,
|
||||
double-click the icon, and wait for interface to launch. At this point you will
|
||||
Using Finder, locate the interface.app Application in build/interface/Debug,
|
||||
double-click the icon, and wait for interface to launch. At this point you will automatically
|
||||
connect to our default domain: "root.highfidelity.io".
|
||||
|
||||
I'm in-world, what can I do?
|
||||
|
@ -94,13 +94,13 @@ I want to run my own virtual world!
|
|||
|
||||
In order to set up your own virtual world, you need to set up and run your own
|
||||
local "domain". At a minimum, you must run a domain-server, voxel-server,
|
||||
audio-mixer, and avatar-mixer to have a working virtual world. The audio-mixer, avatar-mixer, and voxel-server are assignments given from the domain-server to any assignment-client that reports directly to it.
|
||||
audio-mixer, and avatar-mixer to have a working virtual world. The domain server gives three different types of assignments to the assignment-client: audio-mixer, avatar-mixer and voxel server.
|
||||
|
||||
Complete the steps above to build the system components, using the default Cmake Unix Makefiles generator. Start with an empty build directory.
|
||||
|
||||
cmake ..
|
||||
|
||||
Then from the terminal
|
||||
Then from the Terminal
|
||||
window, change directory into the build directory, make the needed components, and then launch them.
|
||||
|
||||
First we make the targets we'll need.
|
||||
|
@ -114,15 +114,17 @@ If after this step you're seeing something like the following
|
|||
|
||||
you likely had Cmake generate Xcode project files and have not run `cmake ..` in a clean build directory.
|
||||
|
||||
Then, launch the static domain-server. All of the targets will run in the foreground, so you'll either want to background it yourself or open a seperate terminal window per target.
|
||||
Then, launch the static domain-server. All of the targets will run in the foreground, so you'll either want to background it yourself or open a separate terminal window per target.
|
||||
|
||||
cd domain-server && ./domain-server
|
||||
|
||||
Then, run an assignment-client with 3 forks to fulfill the avatar-mixer, audio-mixer, and voxel-server assignments. It uses localhost as its assignment-server and talks to it on port 40102 (the default domain-server port).
|
||||
Then, run an assignment-client with all three necessary components: avatar-mixer, audio-mixer, and voxel-server assignments. The assignment-client uses localhost as its assignment-server and talks to it on port 40102 (the default domain-server port).
|
||||
|
||||
In a new Terminal window, run:
|
||||
|
||||
./assignment-client/assignment-client -n 3
|
||||
|
||||
Any target can be terminated with CTRL-C (SIGINT) in the associated terminal window.
|
||||
Any target can be terminated with Ctrl-C (SIGINT) in the associated Terminal window.
|
||||
|
||||
To test things out you'll want to run the Interface client. You can make that target with the following command:
|
||||
|
||||
|
@ -130,9 +132,9 @@ To test things out you'll want to run the Interface client. You can make that ta
|
|||
|
||||
Then run the executable it builds, or open interface.app if you're on OS X.
|
||||
|
||||
To access your local domain in Interface, open the Preferences dialog box, from
|
||||
the Interface menu on OS X or the File menu on Linux, and enter "localhost" for the
|
||||
server hostname in the "Domain" edit control.
|
||||
To access your local domain in Interface, open your Preferences -- on OS X this is available in the Interface menu, on Linux you'll find it in the File menu. Enter "localhost" in the "Domain server" field.
|
||||
|
||||
If everything worked you should see "Servers: 3" in the upper right. Nice work!
|
||||
|
||||
In the voxel-server/src directory you will find a README that explains in
|
||||
further detail how to setup and administer a voxel-server.
|
BIN
interface/resources/images/eye.png
Normal file
BIN
interface/resources/images/eye.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 862 KiB |
28
interface/resources/shaders/eye.vert
Normal file
28
interface/resources/shaders/eye.vert
Normal file
|
@ -0,0 +1,28 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// eye.vert
|
||||
// vertex shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 9/25/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 normal;
|
||||
|
||||
void main(void) {
|
||||
|
||||
// transform and store the normal for interpolation
|
||||
normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
|
||||
|
||||
// compute standard diffuse lighting per-vertex
|
||||
gl_FrontColor = vec4(gl_Color.rgb * (gl_LightModel.ambient.rgb + gl_LightSource[0].ambient.rgb +
|
||||
gl_LightSource[0].diffuse.rgb * max(0.0, dot(normal, gl_LightSource[0].position))), gl_Color.a);
|
||||
|
||||
// pass along the texture coordinate
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
// use standard pipeline transform
|
||||
gl_Position = ftransform();
|
||||
}
|
|
@ -1798,27 +1798,7 @@ void Application::update(float deltaTime) {
|
|||
_isHoverVoxelSounding = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we are dragging on a voxel, add thrust according to the amount the mouse is dragging
|
||||
const float VOXEL_GRAB_THRUST = 0.0f;
|
||||
if (_mousePressed && (_mouseVoxel.s != 0)) {
|
||||
glm::vec2 mouseDrag(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY);
|
||||
glm::quat orientation = _myAvatar.getOrientation();
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
glm::vec3 towardVoxel = getMouseVoxelWorldCoordinates(_mouseVoxelDragging)
|
||||
- _myAvatar.getCameraPosition();
|
||||
towardVoxel = front * glm::length(towardVoxel);
|
||||
glm::vec3 lateralToVoxel = glm::cross(up, glm::normalize(towardVoxel)) * glm::length(towardVoxel);
|
||||
_voxelThrust = glm::vec3(0, 0, 0);
|
||||
_voxelThrust += towardVoxel * VOXEL_GRAB_THRUST * deltaTime * mouseDrag.y;
|
||||
_voxelThrust += lateralToVoxel * VOXEL_GRAB_THRUST * deltaTime * mouseDrag.x;
|
||||
|
||||
// Add thrust from voxel grabbing to the avatar
|
||||
_myAvatar.addThrust(_voxelThrust);
|
||||
|
||||
}
|
||||
|
||||
_mouseVoxel.s = 0.0f;
|
||||
if (Menu::getInstance()->isVoxelModeActionChecked() &&
|
||||
(fabs(_myAvatar.getVelocity().x) +
|
||||
|
@ -2820,7 +2800,7 @@ void Application::displayStats() {
|
|||
|
||||
char avatarStats[200];
|
||||
glm::vec3 avatarPos = _myAvatar.getPosition();
|
||||
sprintf(avatarStats, "Avatar position: %.3f, %.3f, %.3f, yaw = %.2f", avatarPos.x, avatarPos.y, avatarPos.z, _myAvatar.getBodyYaw());
|
||||
sprintf(avatarStats, "Avatar: pos %.3f, %.3f, %.3f, vel %.1f, yaw = %.2f", avatarPos.x, avatarPos.y, avatarPos.z, glm::length(_myAvatar.getVelocity()), _myAvatar.getBodyYaw());
|
||||
drawtext(10, statsVerticalOffset + 55, 0.10f, 0, 1.0, 0, avatarStats);
|
||||
|
||||
|
||||
|
|
|
@ -297,7 +297,6 @@ private:
|
|||
float _pitchFromTouch;
|
||||
|
||||
VoxelDetail _mouseVoxelDragging;
|
||||
glm::vec3 _voxelThrust;
|
||||
bool _mousePressed; // true if mouse has been pressed (clear when finished)
|
||||
|
||||
VoxelDetail _hoverVoxel; // Stuff about the voxel I am hovering or clicking
|
||||
|
|
|
@ -275,8 +275,6 @@ Menu::Menu() :
|
|||
appInstance->getGlowEffect(),
|
||||
SLOT(cycleRenderMode()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::UseFaceshiftRig, 0, false,
|
||||
appInstance->getFaceshift(), SLOT(setUsingRig(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::UsePerlinFace, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtVectors, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtIndicator, 0, true);
|
||||
|
|
|
@ -199,7 +199,6 @@ namespace MenuOption {
|
|||
const QString TestRaveGlove = "Test Rave Glove";
|
||||
const QString TreeStats = "Calculate Tree Stats";
|
||||
const QString TransmitterDrive = "Transmitter Drive";
|
||||
const QString UseFaceshiftRig = "Use Faceshift Rig";
|
||||
const QString UsePerlinFace = "Use Perlin's Face";
|
||||
const QString Quit = "Quit";
|
||||
const QString Webcam = "Webcam";
|
||||
|
|
|
@ -17,22 +17,39 @@ using namespace std;
|
|||
|
||||
BlendFace::BlendFace(Head* owningHead) :
|
||||
_owningHead(owningHead),
|
||||
_modelReply(NULL),
|
||||
_iboID(0)
|
||||
_modelReply(NULL)
|
||||
{
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
moveToThread(Application::getInstance()->thread());
|
||||
}
|
||||
|
||||
BlendFace::~BlendFace() {
|
||||
if (_iboID != 0) {
|
||||
glDeleteBuffers(1, &_iboID);
|
||||
glDeleteBuffers(1, &_vboID);
|
||||
deleteGeometry();
|
||||
}
|
||||
|
||||
ProgramObject BlendFace::_eyeProgram;
|
||||
GLuint BlendFace::_eyeTextureID;
|
||||
|
||||
void BlendFace::init() {
|
||||
if (!_eyeProgram.isLinked()) {
|
||||
switchToResourcesParentIfRequired();
|
||||
_eyeProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/eye.vert");
|
||||
_eyeProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/iris.frag");
|
||||
_eyeProgram.link();
|
||||
|
||||
_eyeProgram.bind();
|
||||
_eyeProgram.setUniformValue("texture", 0);
|
||||
_eyeProgram.release();
|
||||
|
||||
_eyeTextureID = Application::getInstance()->getTextureCache()->getFileTextureID("resources/images/eye.png");
|
||||
}
|
||||
}
|
||||
|
||||
const glm::vec3 MODEL_TRANSLATION(0.0f, -0.025f, -0.025f); // temporary fudge factor
|
||||
const float MODEL_SCALE = 0.0006f;
|
||||
|
||||
bool BlendFace::render(float alpha) {
|
||||
if (_iboID == 0) {
|
||||
if (_meshIDs.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -40,51 +57,101 @@ bool BlendFace::render(float alpha) {
|
|||
glTranslatef(_owningHead->getPosition().x, _owningHead->getPosition().y, _owningHead->getPosition().z);
|
||||
glm::quat orientation = _owningHead->getOrientation();
|
||||
glm::vec3 axis = glm::axis(orientation);
|
||||
glRotatef(glm::angle(orientation), axis.x, axis.y, axis.z);
|
||||
glTranslatef(0.0f, -0.025f, -0.025f); // temporary fudge factor until we have a better method of per-model positioning
|
||||
const float MODEL_SCALE = 0.0006f;
|
||||
glScalef(_owningHead->getScale() * MODEL_SCALE, _owningHead->getScale() * MODEL_SCALE,
|
||||
glRotatef(glm::angle(orientation), axis.x, axis.y, axis.z);
|
||||
glTranslatef(MODEL_TRANSLATION.x, MODEL_TRANSLATION.y, MODEL_TRANSLATION.z);
|
||||
glm::vec3 scale(-_owningHead->getScale() * MODEL_SCALE, _owningHead->getScale() * MODEL_SCALE,
|
||||
-_owningHead->getScale() * MODEL_SCALE);
|
||||
glScalef(scale.x, scale.y, scale.z);
|
||||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, alpha);
|
||||
|
||||
// start with the base
|
||||
int vertexCount = _geometry.vertices.size();
|
||||
_blendedVertices.resize(vertexCount);
|
||||
memcpy(_blendedVertices.data(), _geometry.vertices.constData(), vertexCount * sizeof(glm::vec3));
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
// blend in each coefficient
|
||||
const vector<float>& coefficients = _owningHead->getBlendshapeCoefficients();
|
||||
for (int i = 0; i < coefficients.size(); i++) {
|
||||
float coefficient = coefficients[i];
|
||||
if (coefficient == 0.0f || i >= _geometry.blendshapes.size() || _geometry.blendshapes[i].vertices.isEmpty()) {
|
||||
continue;
|
||||
// enable normalization under the expectation that the GPU can do it faster
|
||||
glEnable(GL_NORMALIZE);
|
||||
|
||||
glColor4f(_owningHead->getSkinColor().r, _owningHead->getSkinColor().g, _owningHead->getSkinColor().b, alpha);
|
||||
|
||||
for (int i = 0; i < _meshIDs.size(); i++) {
|
||||
const VerticesIndices& ids = _meshIDs.at(i);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ids.first);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, ids.second);
|
||||
|
||||
const FBXMesh& mesh = _geometry.meshes.at(i);
|
||||
int vertexCount = mesh.vertices.size();
|
||||
|
||||
// apply eye rotation if appropriate
|
||||
if (mesh.isEye) {
|
||||
glPushMatrix();
|
||||
glTranslatef(mesh.pivot.x, mesh.pivot.y, mesh.pivot.z);
|
||||
glm::quat rotation = glm::inverse(orientation) * _owningHead->getEyeRotation(orientation *
|
||||
(mesh.pivot * scale + MODEL_TRANSLATION) + _owningHead->getPosition());
|
||||
glm::vec3 rotationAxis = glm::axis(rotation);
|
||||
glRotatef(glm::angle(rotation), -rotationAxis.x, rotationAxis.y, -rotationAxis.z);
|
||||
glTranslatef(-mesh.pivot.x, -mesh.pivot.y, -mesh.pivot.z);
|
||||
|
||||
// use texture coordinates only for the eye, for now
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, _eyeTextureID);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
_eyeProgram.bind();
|
||||
}
|
||||
const glm::vec3* source = _geometry.blendshapes[i].vertices.constData();
|
||||
for (const int* index = _geometry.blendshapes[i].indices.constData(),
|
||||
*end = index + _geometry.blendshapes[i].indices.size(); index != end; index++, source++) {
|
||||
_blendedVertices[*index] += *source * coefficient;
|
||||
|
||||
// all meshes after the first are white
|
||||
if (i == 1) {
|
||||
glColor4f(1.0f, 1.0f, 1.0f, alpha);
|
||||
}
|
||||
|
||||
if (!mesh.blendshapes.isEmpty()) {
|
||||
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount));
|
||||
_blendedNormals.resize(_blendedVertices.size());
|
||||
memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3));
|
||||
memcpy(_blendedNormals.data(), mesh.normals.constData(), vertexCount * sizeof(glm::vec3));
|
||||
|
||||
// blend in each coefficient
|
||||
const vector<float>& coefficients = _owningHead->getBlendshapeCoefficients();
|
||||
for (int i = 0; i < coefficients.size(); i++) {
|
||||
float coefficient = coefficients[i];
|
||||
if (coefficient == 0.0f || i >= mesh.blendshapes.size() || mesh.blendshapes[i].vertices.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
|
||||
float normalCoefficient = coefficient * NORMAL_COEFFICIENT_SCALE;
|
||||
const glm::vec3* vertex = mesh.blendshapes[i].vertices.constData();
|
||||
const glm::vec3* normal = mesh.blendshapes[i].normals.constData();
|
||||
for (const int* index = mesh.blendshapes[i].indices.constData(),
|
||||
*end = index + mesh.blendshapes[i].indices.size(); index != end; index++, vertex++, normal++) {
|
||||
_blendedVertices[*index] += *vertex * coefficient;
|
||||
_blendedNormals[*index] += *normal * normalCoefficient;
|
||||
}
|
||||
}
|
||||
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), _blendedVertices.constData());
|
||||
glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3),
|
||||
vertexCount * sizeof(glm::vec3), _blendedNormals.constData());
|
||||
}
|
||||
|
||||
glVertexPointer(3, GL_FLOAT, 0, 0);
|
||||
glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3)));
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3)));
|
||||
glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, mesh.quadIndices.size(), GL_UNSIGNED_INT, 0);
|
||||
glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, mesh.triangleIndices.size(),
|
||||
GL_UNSIGNED_INT, (void*)(mesh.quadIndices.size() * sizeof(int)));
|
||||
|
||||
if (mesh.isEye) {
|
||||
_eyeProgram.release();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
// update the blended vertices
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), _blendedVertices.constData());
|
||||
|
||||
// tell OpenGL where to find vertex information
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(3, GL_FLOAT, 0, 0);
|
||||
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboID);
|
||||
glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, _geometry.quadIndices.size(), GL_UNSIGNED_INT, 0);
|
||||
glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, _geometry.triangleIndices.size(), GL_UNSIGNED_INT,
|
||||
(void*)(_geometry.quadIndices.size() * sizeof(int)));
|
||||
glDisable(GL_NORMALIZE);
|
||||
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
|
||||
// deactivate vertex arrays after drawing
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
// bind with 0 to switch back to normal operation
|
||||
|
@ -96,6 +163,25 @@ bool BlendFace::render(float alpha) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void BlendFace::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
||||
glm::quat orientation = _owningHead->getOrientation();
|
||||
glm::vec3 scale(-_owningHead->getScale() * MODEL_SCALE, _owningHead->getScale() * MODEL_SCALE,
|
||||
-_owningHead->getScale() * MODEL_SCALE);
|
||||
bool foundFirst = false;
|
||||
|
||||
foreach (const FBXMesh& mesh, _geometry.meshes) {
|
||||
if (mesh.isEye) {
|
||||
glm::vec3 position = orientation * (mesh.pivot * scale + MODEL_TRANSLATION) + _owningHead->getPosition();
|
||||
if (foundFirst) {
|
||||
secondEyePosition = position;
|
||||
return;
|
||||
}
|
||||
firstEyePosition = position;
|
||||
foundFirst = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlendFace::setModelURL(const QUrl& url) {
|
||||
// don't restart the download if it's the same URL
|
||||
if (_modelURL == url) {
|
||||
|
@ -129,41 +215,6 @@ glm::vec3 createVec3(const fsVector3f& vector) {
|
|||
return glm::vec3(vector.x, vector.y, vector.z);
|
||||
}
|
||||
|
||||
void BlendFace::setRig(const fsMsgRig& rig) {
|
||||
// convert to FBX geometry
|
||||
FBXGeometry geometry;
|
||||
|
||||
for (vector<fsVector4i>::const_iterator it = rig.mesh().m_quads.begin(), end = rig.mesh().m_quads.end(); it != end; it++) {
|
||||
geometry.quadIndices.append(it->x);
|
||||
geometry.quadIndices.append(it->y);
|
||||
geometry.quadIndices.append(it->z);
|
||||
geometry.quadIndices.append(it->w);
|
||||
}
|
||||
|
||||
for (vector<fsVector3i>::const_iterator it = rig.mesh().m_tris.begin(), end = rig.mesh().m_tris.end(); it != end; it++) {
|
||||
geometry.triangleIndices.append(it->x);
|
||||
geometry.triangleIndices.append(it->y);
|
||||
geometry.triangleIndices.append(it->z);
|
||||
}
|
||||
|
||||
for (vector<fsVector3f>::const_iterator it = rig.mesh().m_vertex_data.m_vertices.begin(),
|
||||
end = rig.mesh().m_vertex_data.m_vertices.end(); it != end; it++) {
|
||||
geometry.vertices.append(glm::vec3(it->x, it->y, it->z));
|
||||
}
|
||||
|
||||
for (vector<fsVertexData>::const_iterator it = rig.blendshapes().begin(), end = rig.blendshapes().end(); it != end; it++) {
|
||||
FBXBlendshape blendshape;
|
||||
for (int i = 0, n = it->m_vertices.size(); i < n; i++) {
|
||||
// subtract the base vertex position; we want the deltas
|
||||
blendshape.vertices.append(createVec3(it->m_vertices[i]) - geometry.vertices[i]);
|
||||
blendshape.indices.append(i);
|
||||
}
|
||||
geometry.blendshapes.append(blendshape);
|
||||
}
|
||||
|
||||
setGeometry(geometry);
|
||||
}
|
||||
|
||||
void BlendFace::handleModelDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
||||
if (bytesReceived < bytesTotal && !_modelReply->isFinished()) {
|
||||
return;
|
||||
|
@ -192,30 +243,52 @@ void BlendFace::handleModelReplyError() {
|
|||
}
|
||||
|
||||
void BlendFace::setGeometry(const FBXGeometry& geometry) {
|
||||
if (geometry.vertices.isEmpty()) {
|
||||
// clear any existing geometry
|
||||
if (_iboID != 0) {
|
||||
glDeleteBuffers(1, &_iboID);
|
||||
glDeleteBuffers(1, &_vboID);
|
||||
_iboID = 0;
|
||||
}
|
||||
// clear any existing geometry
|
||||
deleteGeometry();
|
||||
|
||||
if (geometry.meshes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (_iboID == 0) {
|
||||
glGenBuffers(1, &_iboID);
|
||||
glGenBuffers(1, &_vboID);
|
||||
foreach (const FBXMesh& mesh, geometry.meshes) {
|
||||
VerticesIndices ids;
|
||||
glGenBuffers(1, &ids.first);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ids.first);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (mesh.quadIndices.size() + mesh.triangleIndices.size()) * sizeof(int),
|
||||
NULL, GL_STATIC_DRAW);
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mesh.quadIndices.size() * sizeof(int), mesh.quadIndices.constData());
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, mesh.quadIndices.size() * sizeof(int),
|
||||
mesh.triangleIndices.size() * sizeof(int), mesh.triangleIndices.constData());
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
glGenBuffers(1, &ids.second);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, ids.second);
|
||||
|
||||
if (mesh.blendshapes.isEmpty()) {
|
||||
glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3) +
|
||||
mesh.texCoords.size() * sizeof(glm::vec2), 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());
|
||||
|
||||
} else {
|
||||
glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3),
|
||||
NULL, GL_DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
_meshIDs.append(ids);
|
||||
}
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboID);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (geometry.quadIndices.size() + geometry.triangleIndices.size()) * sizeof(int),
|
||||
NULL, GL_STATIC_DRAW);
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, geometry.quadIndices.size() * sizeof(int), geometry.quadIndices.constData());
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, geometry.quadIndices.size() * sizeof(int),
|
||||
geometry.triangleIndices.size() * sizeof(int), geometry.triangleIndices.constData());
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
|
||||
glBufferData(GL_ARRAY_BUFFER, geometry.vertices.size() * sizeof(glm::vec3), NULL, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
_geometry = geometry;
|
||||
}
|
||||
|
||||
void BlendFace::deleteGeometry() {
|
||||
foreach (const VerticesIndices& meshIDs, _meshIDs) {
|
||||
glDeleteBuffers(1, &meshIDs.first);
|
||||
glDeleteBuffers(1, &meshIDs.second);
|
||||
}
|
||||
_meshIDs.clear();
|
||||
}
|
||||
|
|
|
@ -12,10 +12,9 @@
|
|||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
#include <fsbinarystream.h>
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
#include "renderer/FBXReader.h"
|
||||
#include "renderer/ProgramObject.h"
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
|
@ -30,16 +29,15 @@ public:
|
|||
BlendFace(Head* owningHead);
|
||||
~BlendFace();
|
||||
|
||||
bool isActive() const { return _iboID != 0; }
|
||||
bool isActive() const { return !_meshIDs.isEmpty(); }
|
||||
|
||||
void init();
|
||||
bool render(float alpha);
|
||||
|
||||
Q_INVOKABLE void setModelURL(const QUrl& url);
|
||||
const QUrl& getModelURL() const { return _modelURL; }
|
||||
|
||||
public slots:
|
||||
|
||||
void setRig(const fs::fsMsgRig& rig);
|
||||
|
||||
void getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||
|
||||
private slots:
|
||||
|
||||
|
@ -49,6 +47,7 @@ private slots:
|
|||
private:
|
||||
|
||||
void setGeometry(const FBXGeometry& geometry);
|
||||
void deleteGeometry();
|
||||
|
||||
Head* _owningHead;
|
||||
|
||||
|
@ -56,11 +55,15 @@ private:
|
|||
|
||||
QNetworkReply* _modelReply;
|
||||
|
||||
GLuint _iboID;
|
||||
GLuint _vboID;
|
||||
typedef QPair<GLuint, GLuint> VerticesIndices;
|
||||
QVector<VerticesIndices> _meshIDs;
|
||||
|
||||
FBXGeometry _geometry;
|
||||
QVector<glm::vec3> _blendedVertices;
|
||||
QVector<glm::vec3> _blendedNormals;
|
||||
|
||||
static ProgramObject _eyeProgram;
|
||||
static GLuint _eyeTextureID;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__BlendFace__) */
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <QImage>
|
||||
|
||||
#include <NodeList.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
@ -105,16 +103,14 @@ void Head::init() {
|
|||
_irisProgram.setUniformValue("texture", 0);
|
||||
_eyePositionLocation = _irisProgram.uniformLocation("eyePosition");
|
||||
|
||||
QImage image = QImage(IRIS_TEXTURE_FILENAME).convertToFormat(QImage::Format_ARGB32);
|
||||
|
||||
glGenTextures(1, &_irisTextureID);
|
||||
_irisTextureID = Application::getInstance()->getTextureCache()->getFileTextureID(IRIS_TEXTURE_FILENAME);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, _irisTextureID);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1, GL_BGRA, GL_UNSIGNED_BYTE, image.constBits());
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
_blendFace.init();
|
||||
}
|
||||
|
||||
void Head::reset() {
|
||||
|
@ -347,7 +343,13 @@ void Head::render(float alpha, bool isMine) {
|
|||
}
|
||||
|
||||
if (_renderLookatVectors) {
|
||||
renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);
|
||||
glm::vec3 firstEyePosition = _leftEyePosition;
|
||||
glm::vec3 secondEyePosition = _rightEyePosition;
|
||||
if (_blendFace.isActive()) {
|
||||
// the blend face may have custom eye meshes
|
||||
_blendFace.getEyePositions(firstEyePosition, secondEyePosition);
|
||||
}
|
||||
renderLookatVectors(firstEyePosition, secondEyePosition, _lookAtPosition);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -459,6 +461,11 @@ glm::quat Head::getCameraOrientation () const {
|
|||
* glm::quat(glm::radians(glm::vec3(_cameraPitch + _mousePitch, _cameraYaw, 0.0f)));
|
||||
}
|
||||
|
||||
glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const {
|
||||
glm::quat orientation = getOrientation();
|
||||
return rotationBetween(orientation * IDENTITY_FRONT, _lookAtPosition + _saccade - eyePosition) * orientation;
|
||||
}
|
||||
|
||||
void Head::renderHeadSphere() {
|
||||
glPushMatrix();
|
||||
glTranslatef(_position.x, _position.y, _position.z); //translate to head position
|
||||
|
@ -663,17 +670,13 @@ void Head::renderEyeBalls() {
|
|||
glBindTexture(GL_TEXTURE_2D, _irisTextureID);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
|
||||
// render left iris
|
||||
glm::quat leftIrisRotation;
|
||||
glPushMatrix(); {
|
||||
glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); //translate to eyeball position
|
||||
|
||||
//rotate the eyeball to aim towards the lookat position
|
||||
glm::vec3 targetLookatVector = _lookAtPosition + _saccade - _leftEyePosition;
|
||||
leftIrisRotation = rotationBetween(front, targetLookatVector) * orientation;
|
||||
leftIrisRotation = getEyeRotation(_leftEyePosition);
|
||||
glm::vec3 rotationAxis = glm::axis(leftIrisRotation);
|
||||
glRotatef(glm::angle(leftIrisRotation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
|
||||
glTranslatef(0.0f, 0.0f, -_scale * IRIS_PROTRUSION);
|
||||
|
@ -697,8 +700,7 @@ void Head::renderEyeBalls() {
|
|||
glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); //translate to eyeball position
|
||||
|
||||
//rotate the eyeball to aim towards the lookat position
|
||||
glm::vec3 targetLookatVector = _lookAtPosition + _saccade - _rightEyePosition;
|
||||
rightIrisRotation = rotationBetween(front, targetLookatVector) * orientation;
|
||||
rightIrisRotation = getEyeRotation(_rightEyePosition);
|
||||
glm::vec3 rotationAxis = glm::axis(rightIrisRotation);
|
||||
glRotatef(glm::angle(rightIrisRotation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
|
||||
glTranslatef(0.0f, 0.0f, -_scale * IRIS_PROTRUSION);
|
||||
|
|
|
@ -66,11 +66,14 @@ public:
|
|||
|
||||
float getScale() const { return _scale; }
|
||||
glm::vec3 getPosition() const { return _position; }
|
||||
const glm::vec3& getSkinColor() const { return _skinColor; }
|
||||
const glm::vec3& getEyePosition() const { return _eyePosition; }
|
||||
glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||
|
||||
glm::quat getEyeRotation(const glm::vec3& eyePosition) const;
|
||||
|
||||
Face& getFace() { return _face; }
|
||||
BlendFace& getBlendFace() { return _blendFace; }
|
||||
|
||||
|
|
|
@ -50,7 +50,8 @@ MyAvatar::MyAvatar(Node* owningNode) :
|
|||
_elapsedTimeSinceCollision(0.0f),
|
||||
_lastCollisionPosition(0, 0, 0),
|
||||
_speedBrakes(false),
|
||||
_isThrustOn(false)
|
||||
_isThrustOn(false),
|
||||
_thrustMultiplier(1.0f)
|
||||
{
|
||||
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
|
||||
_driveKeys[i] = false;
|
||||
|
@ -59,14 +60,6 @@ MyAvatar::MyAvatar(Node* owningNode) :
|
|||
_collisionRadius = _height * COLLISION_RADIUS_SCALE;
|
||||
}
|
||||
|
||||
void MyAvatar::init() {
|
||||
Avatar::init();
|
||||
|
||||
// when we receive a Faceshift rig, apply it to our own blend face
|
||||
_head.getBlendFace().connect(Application::getInstance()->getFaceshift(), SIGNAL(rigReceived(fs::fsMsgRig)),
|
||||
SLOT(setRig(fs::fsMsgRig)));
|
||||
}
|
||||
|
||||
void MyAvatar::reset() {
|
||||
_head.reset();
|
||||
_hand.reset();
|
||||
|
@ -218,9 +211,10 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCam
|
|||
const float STATIC_FRICTION_STRENGTH = _scale * 20.f;
|
||||
applyStaticFriction(deltaTime, _velocity, MAX_STATIC_FRICTION_VELOCITY, STATIC_FRICTION_STRENGTH);
|
||||
|
||||
const float LINEAR_DAMPING_STRENGTH = 1.0f;
|
||||
const float LINEAR_DAMPING_STRENGTH = 0.5f;
|
||||
const float SPEED_BRAKE_POWER = _scale * 10.0f;
|
||||
const float SQUARED_DAMPING_STRENGTH = 0.2f;
|
||||
// Note: PER changed squared damping strength to zero
|
||||
const float SQUARED_DAMPING_STRENGTH = 0.0f;
|
||||
if (_speedBrakes) {
|
||||
applyDamping(deltaTime, _velocity, LINEAR_DAMPING_STRENGTH * SPEED_BRAKE_POWER, SQUARED_DAMPING_STRENGTH * SPEED_BRAKE_POWER);
|
||||
} else {
|
||||
|
@ -634,15 +628,23 @@ void MyAvatar::updateThrust(float deltaTime, Transmitter * transmitter) {
|
|||
const float THRUST_JUMP = 120.f;
|
||||
|
||||
// Add Thrusts from keyboard
|
||||
if (_driveKeys[FWD]) {_thrust += _scale * THRUST_MAG_FWD * deltaTime * front;}
|
||||
if (_driveKeys[BACK]) {_thrust -= _scale * THRUST_MAG_BACK * deltaTime * front;}
|
||||
if (_driveKeys[RIGHT]) {_thrust += _scale * THRUST_MAG_LATERAL * deltaTime * right;}
|
||||
if (_driveKeys[LEFT]) {_thrust -= _scale * THRUST_MAG_LATERAL * deltaTime * right;}
|
||||
if (_driveKeys[UP]) {_thrust += _scale * THRUST_MAG_UP * deltaTime * up;}
|
||||
if (_driveKeys[DOWN]) {_thrust -= _scale * THRUST_MAG_DOWN * deltaTime * up;}
|
||||
if (_driveKeys[FWD]) {_thrust += _scale * THRUST_MAG_FWD * _thrustMultiplier * deltaTime * front;}
|
||||
if (_driveKeys[BACK]) {_thrust -= _scale * THRUST_MAG_BACK * _thrustMultiplier * deltaTime * front;}
|
||||
if (_driveKeys[RIGHT]) {_thrust += _scale * THRUST_MAG_LATERAL * _thrustMultiplier * deltaTime * right;}
|
||||
if (_driveKeys[LEFT]) {_thrust -= _scale * THRUST_MAG_LATERAL * _thrustMultiplier * deltaTime * right;}
|
||||
if (_driveKeys[UP]) {_thrust += _scale * THRUST_MAG_UP * _thrustMultiplier * deltaTime * up;}
|
||||
if (_driveKeys[DOWN]) {_thrust -= _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up;}
|
||||
if (_driveKeys[ROT_RIGHT]) {_bodyYawDelta -= YAW_MAG * deltaTime;}
|
||||
if (_driveKeys[ROT_LEFT]) {_bodyYawDelta += YAW_MAG * deltaTime;}
|
||||
|
||||
// If thrust keys are being held down, slowly increase thrust to allow reaching great speeds
|
||||
if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) {
|
||||
const float THRUST_INCREASE_RATE = 1.0;
|
||||
_thrustMultiplier *= 1.f + deltaTime * THRUST_INCREASE_RATE;
|
||||
} else {
|
||||
_thrustMultiplier = 1.f;
|
||||
}
|
||||
|
||||
// Add one time jumping force if requested
|
||||
if (_shouldJump) {
|
||||
_thrust += _scale * THRUST_JUMP * up;
|
||||
|
|
|
@ -15,7 +15,6 @@ class MyAvatar : public Avatar {
|
|||
public:
|
||||
MyAvatar(Node* owningNode = NULL);
|
||||
|
||||
void init();
|
||||
void reset();
|
||||
void simulate(float deltaTime, Transmitter* transmitter, float gyroCameraSensitivity);
|
||||
void updateFromGyrosAndOrWebcam(bool gyroLook, float pitchFromTouch);
|
||||
|
@ -72,6 +71,7 @@ public:
|
|||
glm::vec3 _lastCollisionPosition;
|
||||
bool _speedBrakes;
|
||||
bool _isThrustOn;
|
||||
float _thrustMultiplier;
|
||||
float _collisionRadius;
|
||||
|
||||
// private methods
|
||||
|
|
|
@ -20,7 +20,7 @@ const quint16 FACESHIFT_PORT = 33433;
|
|||
|
||||
Faceshift::Faceshift() :
|
||||
_tcpEnabled(false),
|
||||
_lastMessageReceived(0),
|
||||
_lastTrackingStateReceived(0),
|
||||
_eyeGazeLeftPitch(0.0f),
|
||||
_eyeGazeLeftYaw(0.0f),
|
||||
_eyeGazeRightPitch(0.0f),
|
||||
|
@ -53,8 +53,7 @@ Faceshift::Faceshift() :
|
|||
|
||||
bool Faceshift::isActive() const {
|
||||
const uint64_t ACTIVE_TIMEOUT_USECS = 1000000;
|
||||
return (_tcpSocket.state() == QAbstractSocket::ConnectedState ||
|
||||
(usecTimestampNow() - _lastMessageReceived) < ACTIVE_TIMEOUT_USECS) && _tracking;
|
||||
return (usecTimestampNow() - _lastTrackingStateReceived) < ACTIVE_TIMEOUT_USECS;
|
||||
}
|
||||
|
||||
void Faceshift::update() {
|
||||
|
@ -87,17 +86,6 @@ void Faceshift::setTCPEnabled(bool enabled) {
|
|||
}
|
||||
}
|
||||
|
||||
void Faceshift::setUsingRig(bool usingRig) {
|
||||
if (usingRig && _tcpSocket.state() == QAbstractSocket::ConnectedState) {
|
||||
string message;
|
||||
fsBinaryStream::encode_message(message, fsMsgSendRig());
|
||||
send(message);
|
||||
|
||||
} else {
|
||||
emit rigReceived(fsMsgRig());
|
||||
}
|
||||
}
|
||||
|
||||
void Faceshift::connectSocket() {
|
||||
if (_tcpEnabled) {
|
||||
qDebug("Faceshift: Connecting...\n");
|
||||
|
@ -114,11 +102,6 @@ void Faceshift::noteConnected() {
|
|||
string message;
|
||||
fsBinaryStream::encode_message(message, fsMsgSendBlendshapeNames());
|
||||
send(message);
|
||||
|
||||
// if using faceshift rig, request it
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::UseFaceshiftRig)) {
|
||||
setUsingRig(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Faceshift::noteError(QAbstractSocket::SocketError error) {
|
||||
|
@ -169,6 +152,8 @@ void Faceshift::receive(const QByteArray& buffer) {
|
|||
_eyeGazeRightPitch = -data.m_eyeGazeRightPitch;
|
||||
_eyeGazeRightYaw = data.m_eyeGazeRightYaw;
|
||||
_blendshapeCoefficients = data.m_coeffs;
|
||||
|
||||
_lastTrackingStateReceived = usecTimestampNow();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -214,14 +199,8 @@ void Faceshift::receive(const QByteArray& buffer) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case fsMsg::MSG_OUT_RIG: {
|
||||
fsMsgRig* rig = static_cast<fsMsgRig*>(msg.get());
|
||||
emit rigReceived(*rig);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
_lastMessageReceived = usecTimestampNow();
|
||||
}
|
||||
|
|
|
@ -60,15 +60,10 @@ public:
|
|||
|
||||
void update();
|
||||
void reset();
|
||||
|
||||
signals:
|
||||
|
||||
void rigReceived(const fs::fsMsgRig& rig);
|
||||
|
||||
public slots:
|
||||
|
||||
void setTCPEnabled(bool enabled);
|
||||
void setUsingRig(bool usingRig);
|
||||
|
||||
private slots:
|
||||
|
||||
|
@ -90,7 +85,7 @@ private:
|
|||
fs::fsBinaryStream _stream;
|
||||
bool _tcpEnabled;
|
||||
bool _tracking;
|
||||
uint64_t _lastMessageReceived;
|
||||
uint64_t _lastTrackingStateReceived;
|
||||
|
||||
glm::quat _headRotation;
|
||||
glm::vec3 _headTranslation;
|
||||
|
|
|
@ -189,7 +189,20 @@ FBXNode parseFBX(QIODevice* device) {
|
|||
QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) {
|
||||
QVector<glm::vec3> values;
|
||||
for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) {
|
||||
values.append(glm::vec3(*it++, *it++, *it++));
|
||||
float x = *it++;
|
||||
float y = *it++;
|
||||
float z = *it++;
|
||||
values.append(glm::vec3(x, y, z));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
QVector<glm::vec2> createVec2Vector(const QVector<double>& doubleVector) {
|
||||
QVector<glm::vec2> values;
|
||||
for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) {
|
||||
float s = *it++;
|
||||
float t = *it++;
|
||||
values.append(glm::vec2(s, t));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
@ -259,21 +272,33 @@ QHash<QByteArray, int> createBlendshapeMap() {
|
|||
}
|
||||
}
|
||||
|
||||
class ExtractedBlendshape {
|
||||
public:
|
||||
qint64 id;
|
||||
int index;
|
||||
FBXBlendshape blendshape;
|
||||
};
|
||||
|
||||
FBXGeometry extractFBXGeometry(const FBXNode& node) {
|
||||
QVector<FBXBlendshape> blendshapes;
|
||||
QHash<qint64, FBXGeometry> meshMap;
|
||||
qint64 blendshapeId = 0;
|
||||
QHash<qint64, FBXMesh> meshes;
|
||||
QVector<ExtractedBlendshape> blendshapes;
|
||||
QHash<qint64, qint64> parentMap;
|
||||
QMultiHash<qint64, qint64> childMap;
|
||||
QHash<qint64, glm::vec3> pivots;
|
||||
qint64 jointEyeLeftID = 0;
|
||||
qint64 jointEyeRightID = 0;
|
||||
|
||||
foreach (const FBXNode& child, node.children) {
|
||||
if (child.name == "Objects") {
|
||||
foreach (const FBXNode& object, child.children) {
|
||||
if (object.name == "Geometry") {
|
||||
if (object.properties.at(2) == "Mesh") {
|
||||
FBXGeometry mesh;
|
||||
FBXMesh mesh;
|
||||
|
||||
QVector<glm::vec3> normals;
|
||||
QVector<int> polygonIndices;
|
||||
QVector<glm::vec3> normals;
|
||||
QVector<glm::vec2> texCoords;
|
||||
QVector<int> texCoordIndices;
|
||||
foreach (const FBXNode& data, object.children) {
|
||||
if (data.name == "Vertices") {
|
||||
mesh.vertices = createVec3Vector(data.properties.at(0).value<QVector<double> >());
|
||||
|
@ -282,19 +307,48 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
|
|||
polygonIndices = data.properties.at(0).value<QVector<int> >();
|
||||
|
||||
} else if (data.name == "LayerElementNormal") {
|
||||
bool byVertex = false;
|
||||
foreach (const FBXNode& subdata, data.children) {
|
||||
if (subdata.name == "Normals") {
|
||||
normals = createVec3Vector(subdata.properties.at(0).value<QVector<double> >());
|
||||
|
||||
} else if (subdata.name == "MappingInformationType" &&
|
||||
subdata.properties.at(0) == "ByVertice") {
|
||||
byVertex = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (byVertex) {
|
||||
mesh.normals = normals;
|
||||
}
|
||||
} else if (data.name == "LayerElementUV" && data.properties.at(0).toInt() == 0) {
|
||||
foreach (const FBXNode& subdata, data.children) {
|
||||
if (subdata.name == "UV") {
|
||||
texCoords = createVec2Vector(subdata.properties.at(0).value<QVector<double> >());
|
||||
|
||||
} else if (subdata.name == "UVIndex") {
|
||||
texCoordIndices = subdata.properties.at(0).value<QVector<int> >();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the (base) normals correspond to the polygon indices, for some reason
|
||||
mesh.normals.resize(mesh.vertices.size());
|
||||
// convert normals from per-index to per-vertex if necessary
|
||||
if (mesh.normals.isEmpty()) {
|
||||
mesh.normals.resize(mesh.vertices.size());
|
||||
for (int i = 0, n = polygonIndices.size(); i < n; i++) {
|
||||
int index = polygonIndices.at(i);
|
||||
mesh.normals[index < 0 ? (-index - 1) : index] = normals.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
// same with the tex coords
|
||||
mesh.texCoords.resize(mesh.vertices.size());
|
||||
for (int i = 0, n = polygonIndices.size(); i < n; i++) {
|
||||
int index = polygonIndices.at(i);
|
||||
mesh.normals[index < 0 ? (-index - 1) : index] = normals[i];
|
||||
int texCoordIndex = texCoordIndices.at(i);
|
||||
if (texCoordIndex >= 0) {
|
||||
mesh.texCoords[index < 0 ? (-index - 1) : index] = texCoords.at(texCoordIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// convert the polygons to quads and triangles
|
||||
|
@ -323,50 +377,96 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
|
|||
beginIndex = endIndex;
|
||||
}
|
||||
}
|
||||
meshMap.insert(object.properties.at(0).value<qint64>(), mesh);
|
||||
meshes.insert(object.properties.at(0).value<qint64>(), mesh);
|
||||
|
||||
} else { // object.properties.at(2) == "Shape"
|
||||
FBXBlendshape blendshape;
|
||||
ExtractedBlendshape extracted = { object.properties.at(0).value<qint64>() };
|
||||
|
||||
foreach (const FBXNode& data, object.children) {
|
||||
if (data.name == "Indexes") {
|
||||
blendshape.indices = data.properties.at(0).value<QVector<int> >();
|
||||
extracted.blendshape.indices = data.properties.at(0).value<QVector<int> >();
|
||||
|
||||
} else if (data.name == "Vertices") {
|
||||
blendshape.vertices = createVec3Vector(data.properties.at(0).value<QVector<double> >());
|
||||
extracted.blendshape.vertices = createVec3Vector(
|
||||
data.properties.at(0).value<QVector<double> >());
|
||||
|
||||
} else if (data.name == "Normals") {
|
||||
blendshape.normals = createVec3Vector(data.properties.at(0).value<QVector<double> >());
|
||||
extracted.blendshape.normals = createVec3Vector(
|
||||
data.properties.at(0).value<QVector<double> >());
|
||||
}
|
||||
}
|
||||
|
||||
// the name is followed by a null and some type info
|
||||
QByteArray name = object.properties.at(1).toByteArray();
|
||||
static QHash<QByteArray, int> blendshapeMap = createBlendshapeMap();
|
||||
int index = blendshapeMap.value(name.left(name.indexOf('\0')));
|
||||
blendshapes.resize(qMax(blendshapes.size(), index + 1));
|
||||
blendshapes[index] = blendshape;
|
||||
extracted.index = blendshapeMap.value(name.left(name.indexOf('\0')));
|
||||
|
||||
blendshapes.append(extracted);
|
||||
}
|
||||
} else if (object.name == "Model" && object.properties.at(2) == "LimbNode") {
|
||||
if (object.properties.at(1).toByteArray().startsWith("jointEyeLeft")) {
|
||||
jointEyeLeftID = object.properties.at(0).value<qint64>();
|
||||
|
||||
} else if (object.properties.at(1).toByteArray().startsWith("jointEyeRight")) {
|
||||
jointEyeRightID = object.properties.at(0).value<qint64>();
|
||||
}
|
||||
} else if (object.name == "Deformer" && object.properties.at(2) == "Cluster") {
|
||||
foreach (const FBXNode& subobject, object.children) {
|
||||
if (subobject.name == "TransformLink") {
|
||||
QVector<double> values = subobject.properties.at(0).value<QVector<double> >();
|
||||
pivots.insert(object.properties.at(0).value<qint64>(),
|
||||
glm::vec3(values.at(12), values.at(13), values.at(14))); // matrix translation component
|
||||
}
|
||||
}
|
||||
} else if (object.name == "Deformer" && object.properties.at(2) == "BlendShape") {
|
||||
blendshapeId = object.properties.at(0).value<qint64>();
|
||||
}
|
||||
}
|
||||
} else if (child.name == "Connections") {
|
||||
foreach (const FBXNode& connection, child.children) {
|
||||
if (connection.name == "C") {
|
||||
parentMap.insert(connection.properties.at(1).value<qint64>(), connection.properties.at(2).value<qint64>());
|
||||
childMap.insert(connection.properties.at(2).value<qint64>(), connection.properties.at(1).value<qint64>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get the mesh that owns the blendshape
|
||||
FBXGeometry geometry;
|
||||
if (meshMap.size() == 1) {
|
||||
geometry = *meshMap.begin();
|
||||
} else {
|
||||
geometry = meshMap.take(parentMap.value(blendshapeId));
|
||||
// assign the blendshapes to their corresponding meshes
|
||||
foreach (const ExtractedBlendshape& extracted, blendshapes) {
|
||||
qint64 blendshapeChannelID = parentMap.value(extracted.id);
|
||||
qint64 blendshapeID = parentMap.value(blendshapeChannelID);
|
||||
qint64 meshID = parentMap.value(blendshapeID);
|
||||
FBXMesh& mesh = meshes[meshID];
|
||||
mesh.blendshapes.resize(max(mesh.blendshapes.size(), extracted.index + 1));
|
||||
mesh.blendshapes[extracted.index] = extracted.blendshape;
|
||||
}
|
||||
|
||||
// as a temporary hack, put the mesh with the most blendshapes on top; assume it to be the face
|
||||
FBXGeometry geometry;
|
||||
int mostBlendshapes = 0;
|
||||
for (QHash<qint64, FBXMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) {
|
||||
FBXMesh& mesh = it.value();
|
||||
|
||||
// look for a limb pivot
|
||||
mesh.isEye = false;
|
||||
foreach (qint64 childID, childMap.values(it.key())) {
|
||||
qint64 clusterID = childMap.value(childID);
|
||||
if (pivots.contains(clusterID)) {
|
||||
mesh.pivot = pivots.value(clusterID);
|
||||
qint64 jointID = childMap.value(clusterID);
|
||||
if (jointID == jointEyeLeftID || jointID == jointEyeRightID) {
|
||||
mesh.isEye = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh.blendshapes.size() > mostBlendshapes) {
|
||||
geometry.meshes.prepend(mesh);
|
||||
mostBlendshapes = mesh.blendshapes.size();
|
||||
|
||||
} else {
|
||||
geometry.meshes.append(mesh);
|
||||
}
|
||||
}
|
||||
geometry.blendshapes = blendshapes;
|
||||
|
||||
return geometry;
|
||||
}
|
||||
|
|
|
@ -38,18 +38,30 @@ public:
|
|||
QVector<glm::vec3> normals;
|
||||
};
|
||||
|
||||
/// Base geometry with blendshapes mapped by name.
|
||||
class FBXGeometry {
|
||||
/// A single mesh (with optional blendshapes) extracted from an FBX document.
|
||||
class FBXMesh {
|
||||
public:
|
||||
|
||||
QVector<int> quadIndices;
|
||||
QVector<int> triangleIndices;
|
||||
QVector<glm::vec3> vertices;
|
||||
QVector<glm::vec3> normals;
|
||||
QVector<glm::vec2> texCoords;
|
||||
|
||||
glm::vec3 pivot;
|
||||
|
||||
bool isEye;
|
||||
|
||||
QVector<FBXBlendshape> blendshapes;
|
||||
};
|
||||
|
||||
/// A set of meshes extracted from an FBX document.
|
||||
class FBXGeometry {
|
||||
public:
|
||||
|
||||
QVector<FBXMesh> meshes;
|
||||
};
|
||||
|
||||
/// Parses the input from the supplied data as an FBX file.
|
||||
/// \exception QString if an error occurs in parsing
|
||||
FBXNode parseFBX(const QByteArray& data);
|
||||
|
|
|
@ -24,6 +24,9 @@ TextureCache::~TextureCache() {
|
|||
if (_permutationNormalTextureID != 0) {
|
||||
glDeleteTextures(1, &_permutationNormalTextureID);
|
||||
}
|
||||
foreach (GLuint id, _fileTextureIDs) {
|
||||
glDeleteTextures(1, &id);
|
||||
}
|
||||
if (_primaryFramebufferObject != NULL) {
|
||||
delete _primaryFramebufferObject;
|
||||
glDeleteTextures(1, &_primaryDepthTextureID);
|
||||
|
@ -62,6 +65,24 @@ GLuint TextureCache::getPermutationNormalTextureID() {
|
|||
return _permutationNormalTextureID;
|
||||
}
|
||||
|
||||
GLuint TextureCache::getFileTextureID(const QString& filename) {
|
||||
GLuint id = _fileTextureIDs.value(filename);
|
||||
if (id == 0) {
|
||||
switchToResourcesParentIfRequired();
|
||||
QImage image = QImage(filename).convertToFormat(QImage::Format_ARGB32);
|
||||
|
||||
glGenTextures(1, &id);
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1,
|
||||
GL_BGRA, GL_UNSIGNED_BYTE, image.constBits());
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
_fileTextureIDs.insert(filename, id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
QOpenGLFramebufferObject* TextureCache::getPrimaryFramebufferObject() {
|
||||
if (_primaryFramebufferObject == NULL) {
|
||||
_primaryFramebufferObject = createFramebufferObject();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#ifndef __interface__TextureCache__
|
||||
#define __interface__TextureCache__
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
@ -27,6 +28,9 @@ public:
|
|||
/// the second, a set of random unit vectors to be used as noise gradients.
|
||||
GLuint getPermutationNormalTextureID();
|
||||
|
||||
/// Returns the ID of a texture containing the contents of the specified file, loading it if necessary.
|
||||
GLuint getFileTextureID(const QString& filename);
|
||||
|
||||
/// Returns a pointer to the primary framebuffer object. This render target includes a depth component, and is
|
||||
/// used for scene rendering.
|
||||
QOpenGLFramebufferObject* getPrimaryFramebufferObject();
|
||||
|
@ -50,6 +54,8 @@ private:
|
|||
|
||||
GLuint _permutationNormalTextureID;
|
||||
|
||||
QHash<QString, GLuint> _fileTextureIDs;
|
||||
|
||||
GLuint _primaryDepthTextureID;
|
||||
QOpenGLFramebufferObject* _primaryFramebufferObject;
|
||||
QOpenGLFramebufferObject* _secondaryFramebufferObject;
|
||||
|
|
|
@ -55,6 +55,184 @@ void voxelTutorial(VoxelTree * tree) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void processSplitSVOFile(const char* splitSVOFile,const char* splitJurisdictionRoot,const char* splitJurisdictionEndNodes) {
|
||||
char outputFileName[512];
|
||||
|
||||
printf("splitSVOFile: %s Jurisdictions Root: %s EndNodes: %s\n",
|
||||
splitSVOFile, splitJurisdictionRoot, splitJurisdictionEndNodes);
|
||||
|
||||
VoxelTree rootSVO;
|
||||
|
||||
rootSVO.readFromSVOFile(splitSVOFile);
|
||||
JurisdictionMap jurisdiction(splitJurisdictionRoot, splitJurisdictionEndNodes);
|
||||
|
||||
printf("Jurisdiction Root Octcode: ");
|
||||
printOctalCode(jurisdiction.getRootOctalCode());
|
||||
|
||||
printf("Jurisdiction End Nodes: %d \n", jurisdiction.getEndNodeCount());
|
||||
for (int i = 0; i < jurisdiction.getEndNodeCount(); i++) {
|
||||
unsigned char* endNodeCode = jurisdiction.getEndNodeOctalCode(i);
|
||||
printf("End Node: %d ", i);
|
||||
printOctalCode(endNodeCode);
|
||||
|
||||
// get the endNode details
|
||||
VoxelPositionSize endNodeDetails;
|
||||
voxelDetailsForCode(endNodeCode, endNodeDetails);
|
||||
|
||||
// Now, create a split SVO for the EndNode.
|
||||
// copy the EndNode into a temporary tree
|
||||
VoxelTree endNodeTree;
|
||||
|
||||
// create a small voxels at corners of the endNode Tree, this will is a hack
|
||||
// to work around a bug in voxel server that will send Voxel not exists
|
||||
// for regions that don't contain anything even if they're not in the
|
||||
// jurisdiction of the server
|
||||
// This hack assumes the end nodes for demo dinner since it only guarantees
|
||||
// nodes in the 8 child voxels of the main root voxel
|
||||
const float verySmall = 0.015625;
|
||||
endNodeTree.createVoxel(0.0, 0.0, 0.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(1.0, 0.0, 0.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(0.0, 1.0, 0.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(0.0, 0.0, 1.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(1.0, 1.0, 1.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(1.0, 1.0, 0.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(0.0, 1.0, 1.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(1.0, 0.0, 1.0, verySmall, 1, 1, 1, true);
|
||||
|
||||
// Delete the voxel for the EndNode from the temporary tree, so we can
|
||||
// import our endNode content into it...
|
||||
endNodeTree.deleteVoxelCodeFromTree(endNodeCode, COLLAPSE_EMPTY_TREE);
|
||||
|
||||
VoxelNode* endNode = rootSVO.getVoxelAt(endNodeDetails.x,
|
||||
endNodeDetails.y,
|
||||
endNodeDetails.z,
|
||||
endNodeDetails.s);
|
||||
|
||||
rootSVO.copySubTreeIntoNewTree(endNode, &endNodeTree, false);
|
||||
|
||||
sprintf(outputFileName, "splitENDNODE%d%s", i, splitSVOFile);
|
||||
printf("outputFile: %s\n", outputFileName);
|
||||
endNodeTree.writeToSVOFile(outputFileName);
|
||||
|
||||
// Delete the voxel for the EndNode from the root tree...
|
||||
rootSVO.deleteVoxelCodeFromTree(endNodeCode, COLLAPSE_EMPTY_TREE);
|
||||
|
||||
// create a small voxel in center of each EndNode, this will is a hack
|
||||
// to work around a bug in voxel server that will send Voxel not exists
|
||||
// for regions that don't contain anything even if they're not in the
|
||||
// jurisdiction of the server
|
||||
float x = endNodeDetails.x + endNodeDetails.s * 0.5;
|
||||
float y = endNodeDetails.y + endNodeDetails.s * 0.5;
|
||||
float z = endNodeDetails.z + endNodeDetails.s * 0.5;
|
||||
float s = endNodeDetails.s * verySmall;
|
||||
|
||||
rootSVO.createVoxel(x, y, z, s, 1, 1, 1, true);
|
||||
|
||||
}
|
||||
|
||||
sprintf(outputFileName, "splitROOT%s", splitSVOFile);
|
||||
printf("outputFile: %s\n", outputFileName);
|
||||
rootSVO.writeToSVOFile(outputFileName);
|
||||
|
||||
printf("exiting now\n");
|
||||
}
|
||||
|
||||
class copyAndFillArgs {
|
||||
public:
|
||||
VoxelTree* destinationTree;
|
||||
unsigned long outCount;
|
||||
unsigned long inCount;
|
||||
unsigned long originalCount;
|
||||
|
||||
};
|
||||
|
||||
bool copyAndFillOperation(VoxelNode* node, void* extraData) {
|
||||
copyAndFillArgs* args = (copyAndFillArgs*)extraData;
|
||||
char outputMessage[128];
|
||||
|
||||
args->inCount++;
|
||||
int percentDone = (100*args->inCount/args->originalCount);
|
||||
|
||||
// For each leaf node...
|
||||
if (node->isLeaf()) {
|
||||
// create a copy of the leaf in the copy destination
|
||||
float x = node->getCorner().x;
|
||||
float y = node->getCorner().y;
|
||||
float z = node->getCorner().z;
|
||||
float s = node->getScale();
|
||||
unsigned char red = node->getTrueColor()[RED_INDEX];
|
||||
unsigned char green = node->getTrueColor()[GREEN_INDEX];
|
||||
unsigned char blue = node->getTrueColor()[BLUE_INDEX];
|
||||
bool destructive = true;
|
||||
|
||||
args->destinationTree->createVoxel(x, y, z, s, red, green, blue, destructive);
|
||||
args->outCount++;
|
||||
|
||||
sprintf(outputMessage,"Completed: %d%% (%lu of %lu) - Creating voxel %lu at [%f,%f,%f,%f]",
|
||||
percentDone,args->inCount,args->originalCount,args->outCount,x,y,z,s);
|
||||
printf("%s",outputMessage);
|
||||
for (int b = 0; b < strlen(outputMessage); b++) {
|
||||
printf("\b");
|
||||
}
|
||||
|
||||
// and create same sized leafs from this leaf voxel down to zero in the destination tree
|
||||
for (float yFill = y-s; yFill >= 0.0f; yFill -= s) {
|
||||
args->destinationTree->createVoxel(x, yFill, z, s, red, green, blue, destructive);
|
||||
|
||||
args->outCount++;
|
||||
|
||||
sprintf(outputMessage,"Completed: %d%% (%lu of %lu) - Creating fill voxel %lu at [%f,%f,%f,%f]",
|
||||
percentDone,args->inCount,args->originalCount,args->outCount,x,y,z,s);
|
||||
printf("%s",outputMessage);
|
||||
for (int b = 0; b < strlen(outputMessage); b++) {
|
||||
printf("\b");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void processFillSVOFile(const char* fillSVOFile) {
|
||||
char outputFileName[512];
|
||||
|
||||
printf("fillSVOFile: %s\n", fillSVOFile);
|
||||
|
||||
VoxelTree originalSVO(true); // reaveraging
|
||||
VoxelTree filledSVO(true); // reaveraging
|
||||
|
||||
originalSVO.readFromSVOFile(fillSVOFile);
|
||||
qDebug("Nodes after loading %lu nodes\n", originalSVO.getVoxelCount());
|
||||
originalSVO.reaverageVoxelColors(originalSVO.rootNode);
|
||||
qDebug("Original Voxels reAveraged\n");
|
||||
qDebug("Nodes after reaveraging %lu nodes\n", originalSVO.getVoxelCount());
|
||||
|
||||
copyAndFillArgs args;
|
||||
args.destinationTree = &filledSVO;
|
||||
args.inCount = 0;
|
||||
args.outCount = 0;
|
||||
args.originalCount = originalSVO.getVoxelCount();
|
||||
|
||||
printf("Begin processing...\n");
|
||||
originalSVO.recurseTreeWithOperation(copyAndFillOperation, &args);
|
||||
printf("DONE processing...\n");
|
||||
|
||||
qDebug("Original input nodes used for filling %lu nodes\n", args.originalCount);
|
||||
qDebug("Input nodes traversed during filling %lu nodes\n", args.inCount);
|
||||
qDebug("Nodes created during filling %lu nodes\n", args.outCount);
|
||||
qDebug("Nodes after filling %lu nodes\n", filledSVO.getVoxelCount());
|
||||
|
||||
filledSVO.reaverageVoxelColors(filledSVO.rootNode);
|
||||
qDebug("Nodes after reaveraging %lu nodes\n", filledSVO.getVoxelCount());
|
||||
|
||||
sprintf(outputFileName, "filled%s", fillSVOFile);
|
||||
printf("outputFile: %s\n", outputFileName);
|
||||
filledSVO.writeToSVOFile(outputFileName);
|
||||
|
||||
printf("exiting now\n");
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char * argv[])
|
||||
{
|
||||
qInstallMessageHandler(sharedMessageHandler);
|
||||
|
@ -68,88 +246,19 @@ int main(int argc, const char * argv[])
|
|||
const char* splitJurisdictionRoot = getCmdOption(argc, argv, SPLIT_JURISDICTION_ROOT);
|
||||
const char* splitJurisdictionEndNodes = getCmdOption(argc, argv, SPLIT_JURISDICTION_ENDNODES);
|
||||
if (splitSVOFile && splitJurisdictionRoot && splitJurisdictionEndNodes) {
|
||||
char outputFileName[512];
|
||||
|
||||
printf("splitSVOFile: %s Jurisdictions Root: %s EndNodes: %s\n",
|
||||
splitSVOFile, splitJurisdictionRoot, splitJurisdictionEndNodes);
|
||||
|
||||
VoxelTree rootSVO;
|
||||
|
||||
rootSVO.readFromSVOFile(splitSVOFile);
|
||||
JurisdictionMap jurisdiction(splitJurisdictionRoot, splitJurisdictionEndNodes);
|
||||
|
||||
printf("Jurisdiction Root Octcode: ");
|
||||
printOctalCode(jurisdiction.getRootOctalCode());
|
||||
|
||||
printf("Jurisdiction End Nodes: %d \n", jurisdiction.getEndNodeCount());
|
||||
for (int i = 0; i < jurisdiction.getEndNodeCount(); i++) {
|
||||
unsigned char* endNodeCode = jurisdiction.getEndNodeOctalCode(i);
|
||||
printf("End Node: %d ", i);
|
||||
printOctalCode(endNodeCode);
|
||||
|
||||
// get the endNode details
|
||||
VoxelPositionSize endNodeDetails;
|
||||
voxelDetailsForCode(endNodeCode, endNodeDetails);
|
||||
|
||||
// Now, create a split SVO for the EndNode.
|
||||
// copy the EndNode into a temporary tree
|
||||
VoxelTree endNodeTree;
|
||||
|
||||
// create a small voxels at corners of the endNode Tree, this will is a hack
|
||||
// to work around a bug in voxel server that will send Voxel not exists
|
||||
// for regions that don't contain anything even if they're not in the
|
||||
// jurisdiction of the server
|
||||
// This hack assumes the end nodes for demo dinner since it only guarantees
|
||||
// nodes in the 8 child voxels of the main root voxel
|
||||
const float verySmall = 0.015625;
|
||||
endNodeTree.createVoxel(0.0, 0.0, 0.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(1.0, 0.0, 0.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(0.0, 1.0, 0.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(0.0, 0.0, 1.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(1.0, 1.0, 1.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(1.0, 1.0, 0.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(0.0, 1.0, 1.0, verySmall, 1, 1, 1, true);
|
||||
endNodeTree.createVoxel(1.0, 0.0, 1.0, verySmall, 1, 1, 1, true);
|
||||
|
||||
// Delete the voxel for the EndNode from the temporary tree, so we can
|
||||
// import our endNode content into it...
|
||||
endNodeTree.deleteVoxelCodeFromTree(endNodeCode, COLLAPSE_EMPTY_TREE);
|
||||
|
||||
VoxelNode* endNode = rootSVO.getVoxelAt(endNodeDetails.x,
|
||||
endNodeDetails.y,
|
||||
endNodeDetails.z,
|
||||
endNodeDetails.s);
|
||||
|
||||
rootSVO.copySubTreeIntoNewTree(endNode, &endNodeTree, false);
|
||||
|
||||
sprintf(outputFileName, "splitENDNODE%d%s", i, splitSVOFile);
|
||||
printf("outputFile: %s\n", outputFileName);
|
||||
endNodeTree.writeToSVOFile(outputFileName);
|
||||
|
||||
// Delete the voxel for the EndNode from the root tree...
|
||||
rootSVO.deleteVoxelCodeFromTree(endNodeCode, COLLAPSE_EMPTY_TREE);
|
||||
|
||||
// create a small voxel in center of each EndNode, this will is a hack
|
||||
// to work around a bug in voxel server that will send Voxel not exists
|
||||
// for regions that don't contain anything even if they're not in the
|
||||
// jurisdiction of the server
|
||||
float x = endNodeDetails.x + endNodeDetails.s * 0.5;
|
||||
float y = endNodeDetails.y + endNodeDetails.s * 0.5;
|
||||
float z = endNodeDetails.z + endNodeDetails.s * 0.5;
|
||||
float s = endNodeDetails.s * verySmall;
|
||||
|
||||
rootSVO.createVoxel(x, y, z, s, 1, 1, 1, true);
|
||||
|
||||
}
|
||||
|
||||
sprintf(outputFileName, "splitROOT%s", splitSVOFile);
|
||||
printf("outputFile: %s\n", outputFileName);
|
||||
rootSVO.writeToSVOFile(outputFileName);
|
||||
|
||||
printf("exiting now\n");
|
||||
processSplitSVOFile(splitSVOFile, splitJurisdictionRoot, splitJurisdictionEndNodes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Handles taking an SVO and filling in the empty space below the voxels to make it solid.
|
||||
const char* FILL_SVO = "--fillSVO";
|
||||
const char* fillSVOFile = getCmdOption(argc, argv, FILL_SVO);
|
||||
if (fillSVOFile) {
|
||||
processFillSVOFile(fillSVOFile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* DONT_CREATE_FILE = "--dontCreateSceneFile";
|
||||
bool dontCreateFile = cmdOptionExists(argc, argv, DONT_CREATE_FILE);
|
||||
|
||||
|
|
Loading…
Reference in a new issue