mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-09 05:32:46 +02:00
Merge remote-tracking branch 'upstream/master' into synthesis
This commit is contained in:
commit
6c2dcb86c8
45 changed files with 2164 additions and 1058 deletions
|
@ -17,6 +17,8 @@
|
|||
#include <limits>
|
||||
#include <signal.h>
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
#include <AgentList.h>
|
||||
#include <Agent.h>
|
||||
#include <AgentTypes.h>
|
||||
|
@ -57,13 +59,6 @@ const float BUFFER_SEND_INTERVAL_USECS = (BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SA
|
|||
const long MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
|
||||
const long MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::min();
|
||||
|
||||
const float DISTANCE_SCALE = 2.5f;
|
||||
const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5;
|
||||
const int PHASE_DELAY_AT_90 = 20;
|
||||
|
||||
const float MAX_OFF_AXIS_ATTENUATION = 0.2f;
|
||||
const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f;
|
||||
|
||||
void plateauAdditionOfSamples(int16_t &mixSample, int16_t sampleToAdd) {
|
||||
long sumSample = sampleToAdd + mixSample;
|
||||
|
||||
|
@ -158,9 +153,9 @@ int main(int argc, const char* argv[]) {
|
|||
if (otherAgentBuffer->shouldBeAddedToMix()) {
|
||||
|
||||
float bearingRelativeAngleToSource = 0.f;
|
||||
float attenuationCoefficient = 1.f;
|
||||
float attenuationCoefficient = 1.0f;
|
||||
int numSamplesDelay = 0;
|
||||
float weakChannelAmplitudeRatio = 1.f;
|
||||
float weakChannelAmplitudeRatio = 1.0f;
|
||||
|
||||
stk::FreeVerb* otherAgentFreeVerb = NULL;
|
||||
|
||||
|
@ -215,8 +210,50 @@ int main(int argc, const char* argv[]) {
|
|||
if (otherAgentPosition.z > agentPosition.z) {
|
||||
absoluteAngleToSource = 90 - triangleAngle;
|
||||
} else {
|
||||
absoluteAngleToSource = 90 + triangleAngle;
|
||||
// calculate the angle delivery
|
||||
glm::vec3 rotatedListenerPosition = glm::inverse(otherAgentBuffer->getOrientation())
|
||||
* relativePosition;
|
||||
|
||||
float angleOfDelivery = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f),
|
||||
glm::normalize(rotatedListenerPosition));
|
||||
|
||||
const float MAX_OFF_AXIS_ATTENUATION = 0.2f;
|
||||
const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f;
|
||||
|
||||
offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION +
|
||||
(OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / 90.0f));
|
||||
}
|
||||
|
||||
const float DISTANCE_SCALE = 2.5f;
|
||||
const float GEOMETRIC_AMPLITUDE_SCALAR = 0.3f;
|
||||
const float DISTANCE_LOG_BASE = 2.5f;
|
||||
const float DISTANCE_SCALE_LOG = logf(DISTANCE_SCALE) / logf(DISTANCE_LOG_BASE);
|
||||
|
||||
// calculate the distance coefficient using the distance to this agent
|
||||
distanceCoefficient = powf(GEOMETRIC_AMPLITUDE_SCALAR,
|
||||
DISTANCE_SCALE_LOG +
|
||||
(logf(distanceSquareToSource) / logf(DISTANCE_LOG_BASE)) - 1);
|
||||
distanceCoefficient = std::min(1.0f, distanceCoefficient);
|
||||
|
||||
// off-axis attenuation and spatialization of audio
|
||||
// not performed if listener is inside spherical injector
|
||||
|
||||
// calculate the angle from the source to the listener
|
||||
|
||||
// project the rotated source position vector onto the XZ plane
|
||||
rotatedSourcePosition.y = 0.0f;
|
||||
|
||||
// produce an oriented angle about the y-axis
|
||||
bearingRelativeAngleToSource = glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f),
|
||||
glm::normalize(rotatedSourcePosition),
|
||||
glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5;
|
||||
const int PHASE_DELAY_AT_90 = 20;
|
||||
|
||||
float sinRatio = fabsf(sinf(glm::radians(bearingRelativeAngleToSource)));
|
||||
numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio;
|
||||
weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio);
|
||||
}
|
||||
|
||||
bearingRelativeAngleToSource = absoluteAngleToSource - agentRingBuffer->getBearing();
|
||||
|
@ -270,11 +307,11 @@ int main(int argc, const char* argv[]) {
|
|||
}
|
||||
|
||||
int16_t* goodChannel = bearingRelativeAngleToSource > 0.0f
|
||||
? clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL
|
||||
: clientSamples;
|
||||
int16_t* delayedChannel = bearingRelativeAngleToSource > 0.0f
|
||||
? clientSamples
|
||||
: clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
||||
int16_t* delayedChannel = bearingRelativeAngleToSource > 0.0f
|
||||
? clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL
|
||||
: clientSamples;
|
||||
|
||||
int16_t* delaySamplePointer = otherAgentBuffer->getNextOutput() == otherAgentBuffer->getBuffer()
|
||||
? otherAgentBuffer->getBuffer() + RING_BUFFER_SAMPLES - numSamplesDelay
|
||||
|
@ -347,7 +384,7 @@ int main(int argc, const char* argv[]) {
|
|||
|
||||
agentList->updateAgentWithData(agentAddress, packetData, receivedBytes);
|
||||
|
||||
if (std::isnan(((AudioRingBuffer *)avatarAgent->getLinkedData())->getBearing())) {
|
||||
if (std::isnan(((AudioRingBuffer *)avatarAgent->getLinkedData())->getOrientation().x)) {
|
||||
// kill off this agent - temporary solution to mixer crash on mac sleep
|
||||
avatarAgent->setAlive(false);
|
||||
}
|
||||
|
|
|
@ -27,32 +27,41 @@ const int AVATAR_MIXER_DATA_SEND_INTERVAL_MSECS = 15;
|
|||
|
||||
const int DEFAULT_INJECTOR_VOLUME = 0xFF;
|
||||
|
||||
enum {
|
||||
INJECTOR_POSITION_X,
|
||||
INJECTOR_POSITION_Y,
|
||||
INJECTOR_POSITION_Z,
|
||||
INJECTOR_YAW
|
||||
};
|
||||
|
||||
// Command line parameter defaults
|
||||
bool loopAudio = true;
|
||||
float sleepIntervalMin = 1.00;
|
||||
float sleepIntervalMax = 2.00;
|
||||
char *sourceAudioFile = NULL;
|
||||
const char *allowedParameters = ":rb::t::c::a::f::d:";
|
||||
const char *allowedParameters = ":sb::t::c::a::f::d::r:";
|
||||
float floatArguments[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
unsigned char volume = DEFAULT_INJECTOR_VOLUME;
|
||||
float triggerDistance = 0;
|
||||
float triggerDistance = 0.0f;
|
||||
float radius = 0.0f;
|
||||
|
||||
void usage(void) {
|
||||
std::cout << "High Fidelity - Interface audio injector" << std::endl;
|
||||
std::cout << " -r Random sleep mode. If not specified will default to constant loop." << std::endl;
|
||||
std::cout << " -s Random sleep mode. If not specified will default to constant loop." << std::endl;
|
||||
std::cout << " -b FLOAT Min. number of seconds to sleep. Only valid in random sleep mode. Default 1.0" << std::endl;
|
||||
std::cout << " -t FLOAT Max. number of seconds to sleep. Only valid in random sleep mode. Default 2.0" << std::endl;
|
||||
std::cout << " -c FLOAT,FLOAT,FLOAT,FLOAT X,Y,Z,YAW position in universe where audio will be originating from and direction. Defaults to 0,0,0,0" << std::endl;
|
||||
std::cout << " -a 0-255 Attenuation curve modifier, defaults to 255" << std::endl;
|
||||
std::cout << " -f FILENAME Name of audio source file. Required - RAW format, 22050hz 16bit signed mono" << std::endl;
|
||||
std::cout << " -d FLOAT Trigger distance for injection. If not specified will loop constantly" << std::endl;
|
||||
std::cout << " -r FLOAT Radius for spherical source. If not specified injected audio is point source" << std::endl;
|
||||
}
|
||||
|
||||
bool processParameters(int parameterCount, char* parameterData[]) {
|
||||
int p;
|
||||
while ((p = getopt(parameterCount, parameterData, allowedParameters)) != -1) {
|
||||
switch (p) {
|
||||
case 'r':
|
||||
case 's':
|
||||
::loopAudio = false;
|
||||
std::cout << "[DEBUG] Random sleep mode enabled" << std::endl;
|
||||
break;
|
||||
|
@ -92,6 +101,10 @@ bool processParameters(int parameterCount, char* parameterData[]) {
|
|||
::triggerDistance = atof(optarg);
|
||||
std::cout << "[DEBUG] Trigger distance: " << optarg << std::endl;
|
||||
break;
|
||||
case 'r':
|
||||
::radius = atof(optarg);
|
||||
std::cout << "[DEBUG] Injector radius: " << optarg << std::endl;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
return false;
|
||||
|
@ -160,9 +173,16 @@ int main(int argc, char* argv[]) {
|
|||
// start the agent list thread that will kill off agents when they stop talking
|
||||
agentList->startSilentAgentRemovalThread();
|
||||
|
||||
injector.setPosition(glm::vec3(::floatArguments[0], ::floatArguments[1], ::floatArguments[2]));
|
||||
injector.setBearing(*(::floatArguments + 3));
|
||||
injector.setPosition(glm::vec3(::floatArguments[INJECTOR_POSITION_X],
|
||||
::floatArguments[INJECTOR_POSITION_Y],
|
||||
::floatArguments[INJECTOR_POSITION_Z]));
|
||||
injector.setOrientation(glm::quat(glm::vec3(0.0f, ::floatArguments[INJECTOR_YAW], 0.0f)));
|
||||
injector.setVolume(::volume);
|
||||
|
||||
if (::radius > 0) {
|
||||
// if we were passed a cube side length, give that to the injector
|
||||
injector.setRadius(::radius);
|
||||
}
|
||||
|
||||
// register the callback for agent data creation
|
||||
agentList->linkedDataCreateCallback = createAvatarDataForAgent;
|
||||
|
|
|
@ -63,12 +63,12 @@ if (APPLE)
|
|||
|
||||
endif (APPLE)
|
||||
|
||||
find_package(Qt4 REQUIRED QtCore QtGui QtOpenGL)
|
||||
find_package(Qt4 REQUIRED QtCore QtGui QtNetwork QtOpenGL)
|
||||
include(${QT_USE_FILE})
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${QT_QTGUI_INCLUDE_DIR}")
|
||||
|
||||
# run qt moc on qt-enabled headers
|
||||
qt4_wrap_cpp(INTERFACE_SRCS src/Application.h)
|
||||
qt4_wrap_cpp(INTERFACE_SRCS src/Application.h src/AvatarVoxelSystem.h)
|
||||
|
||||
# create the executable, make it a bundle on OS X
|
||||
add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS})
|
||||
|
|
|
@ -1,106 +1,106 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html:
|
||||
//
|
||||
// NVIDIA Statement on the Software
|
||||
//
|
||||
// The source code provided is freely distributable, so long as the NVIDIA header remains unaltered and user modifications are
|
||||
// detailed.
|
||||
//
|
||||
// No Warranty
|
||||
//
|
||||
// THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA ON THE ENCLOSED CD-ROM ARE PROVIDED "AS IS." NVIDIA DISCLAIMS ALL
|
||||
// WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
// Limitation of Liability
|
||||
//
|
||||
// NVIDIA SHALL NOT BE LIABLE TO ANY USER, DEVELOPER, DEVELOPER'S CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR
|
||||
// UNDER DEVELOPER FOR ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE, DIRECT
|
||||
// OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT, TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF THE ESSENTIAL PURPOSE OF ANY
|
||||
// LIMITED REMEDY. IN NO EVENT SHALL NVIDIA'S AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH
|
||||
// OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS.
|
||||
//
|
||||
|
||||
//
|
||||
// Atmospheric scattering fragment shader
|
||||
//
|
||||
// Author: Sean O'Neil
|
||||
//
|
||||
// Copyright (c) 2004 Sean O'Neil
|
||||
//
|
||||
|
||||
uniform vec3 v3CameraPos; // The camera's current position
|
||||
uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels
|
||||
uniform float fInnerRadius; // The inner (planetary) radius
|
||||
uniform float fKrESun; // Kr * ESun
|
||||
uniform float fKmESun; // Km * ESun
|
||||
uniform float fKr4PI; // Kr * 4 * PI
|
||||
uniform float fKm4PI; // Km * 4 * PI
|
||||
uniform float fScale; // 1 / (fOuterRadius - fInnerRadius)
|
||||
uniform float fScaleDepth; // The scale depth (i.e. the altitude at which the atmosphere's average density is found)
|
||||
uniform float fScaleOverScaleDepth; // fScale / fScaleDepth
|
||||
|
||||
const int nSamples = 2;
|
||||
const float fSamples = 2.0;
|
||||
|
||||
uniform vec3 v3LightPos;
|
||||
uniform float g;
|
||||
uniform float g2;
|
||||
|
||||
varying vec3 position;
|
||||
|
||||
float scale(float fCos)
|
||||
{
|
||||
float x = 1.0 - fCos;
|
||||
return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
|
||||
}
|
||||
|
||||
void main (void)
|
||||
{
|
||||
// Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere)
|
||||
vec3 v3Pos = position;
|
||||
vec3 v3Ray = v3Pos - v3CameraPos;
|
||||
float fFar = length(v3Ray);
|
||||
v3Ray /= fFar;
|
||||
|
||||
// Calculate the ray's starting position, then calculate its scattering offset
|
||||
vec3 v3Start = v3CameraPos;
|
||||
float fHeight = length(v3Start);
|
||||
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
|
||||
float fStartAngle = dot(v3Ray, v3Start) / fHeight;
|
||||
float fStartOffset = fDepth * scale(fStartAngle);
|
||||
|
||||
// Initialize the scattering loop variables
|
||||
//gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
float fSampleLength = fFar / fSamples;
|
||||
float fScaledLength = fSampleLength * fScale;
|
||||
vec3 v3SampleRay = v3Ray * fSampleLength;
|
||||
vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5;
|
||||
|
||||
// Now loop through the sample rays
|
||||
vec3 v3FrontColor = vec3(0.0, 0.0, 0.0);
|
||||
for(int i=0; i<nSamples; i++)
|
||||
{
|
||||
float fHeight = length(v3SamplePoint);
|
||||
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
|
||||
float fLightAngle = dot(v3LightPos, v3SamplePoint) / fHeight;
|
||||
float fCameraAngle = dot((v3Ray), v3SamplePoint) / fHeight * 0.99;
|
||||
float fScatter = (fStartOffset + fDepth * (scale(fLightAngle) - scale(fCameraAngle)));
|
||||
vec3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));
|
||||
v3FrontColor += v3Attenuate * (fDepth * fScaledLength);
|
||||
v3SamplePoint += v3SampleRay;
|
||||
}
|
||||
|
||||
// Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
|
||||
vec3 secondaryFrontColor = v3FrontColor * fKmESun;
|
||||
vec3 frontColor = v3FrontColor * (v3InvWavelength * fKrESun);
|
||||
vec3 v3Direction = v3CameraPos - v3Pos;
|
||||
|
||||
float fCos = dot(v3LightPos, v3Direction) / length(v3Direction);
|
||||
float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);
|
||||
gl_FragColor.rgb = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb;
|
||||
gl_FragColor.a = gl_FragColor.b;
|
||||
}
|
||||
#version 120
|
||||
|
||||
//
|
||||
// For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html:
|
||||
//
|
||||
// NVIDIA Statement on the Software
|
||||
//
|
||||
// The source code provided is freely distributable, so long as the NVIDIA header remains unaltered and user modifications are
|
||||
// detailed.
|
||||
//
|
||||
// No Warranty
|
||||
//
|
||||
// THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA ON THE ENCLOSED CD-ROM ARE PROVIDED "AS IS." NVIDIA DISCLAIMS ALL
|
||||
// WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
// Limitation of Liability
|
||||
//
|
||||
// NVIDIA SHALL NOT BE LIABLE TO ANY USER, DEVELOPER, DEVELOPER'S CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR
|
||||
// UNDER DEVELOPER FOR ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE, DIRECT
|
||||
// OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT, TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF THE ESSENTIAL PURPOSE OF ANY
|
||||
// LIMITED REMEDY. IN NO EVENT SHALL NVIDIA'S AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH
|
||||
// OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS.
|
||||
//
|
||||
|
||||
//
|
||||
// Atmospheric scattering fragment shader
|
||||
//
|
||||
// Author: Sean O'Neil
|
||||
//
|
||||
// Copyright (c) 2004 Sean O'Neil
|
||||
//
|
||||
|
||||
uniform vec3 v3CameraPos; // The camera's current position
|
||||
uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels
|
||||
uniform float fInnerRadius; // The inner (planetary) radius
|
||||
uniform float fKrESun; // Kr * ESun
|
||||
uniform float fKmESun; // Km * ESun
|
||||
uniform float fKr4PI; // Kr * 4 * PI
|
||||
uniform float fKm4PI; // Km * 4 * PI
|
||||
uniform float fScale; // 1 / (fOuterRadius - fInnerRadius)
|
||||
uniform float fScaleDepth; // The scale depth (i.e. the altitude at which the atmosphere's average density is found)
|
||||
uniform float fScaleOverScaleDepth; // fScale / fScaleDepth
|
||||
|
||||
const int nSamples = 2;
|
||||
const float fSamples = 2.0;
|
||||
|
||||
uniform vec3 v3LightPos;
|
||||
uniform float g;
|
||||
uniform float g2;
|
||||
|
||||
varying vec3 position;
|
||||
|
||||
float scale(float fCos)
|
||||
{
|
||||
float x = 1.0 - fCos;
|
||||
return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
|
||||
}
|
||||
|
||||
void main (void)
|
||||
{
|
||||
// Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere)
|
||||
vec3 v3Pos = position;
|
||||
vec3 v3Ray = v3Pos - v3CameraPos;
|
||||
float fFar = length(v3Ray);
|
||||
v3Ray /= fFar;
|
||||
|
||||
// Calculate the ray's starting position, then calculate its scattering offset
|
||||
vec3 v3Start = v3CameraPos;
|
||||
float fHeight = length(v3Start);
|
||||
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
|
||||
float fStartAngle = dot(v3Ray, v3Start) / fHeight;
|
||||
float fStartOffset = fDepth * scale(fStartAngle);
|
||||
|
||||
// Initialize the scattering loop variables
|
||||
//gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
float fSampleLength = fFar / fSamples;
|
||||
float fScaledLength = fSampleLength * fScale;
|
||||
vec3 v3SampleRay = v3Ray * fSampleLength;
|
||||
vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5;
|
||||
|
||||
// Now loop through the sample rays
|
||||
vec3 v3FrontColor = vec3(0.0, 0.0, 0.0);
|
||||
for(int i=0; i<nSamples; i++)
|
||||
{
|
||||
float fHeight = length(v3SamplePoint);
|
||||
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
|
||||
float fLightAngle = dot(v3LightPos, v3SamplePoint) / fHeight;
|
||||
float fCameraAngle = dot((v3Ray), v3SamplePoint) / fHeight * 0.99;
|
||||
float fScatter = (fStartOffset + fDepth * (scale(fLightAngle) - scale(fCameraAngle)));
|
||||
vec3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));
|
||||
v3FrontColor += v3Attenuate * (fDepth * fScaledLength);
|
||||
v3SamplePoint += v3SampleRay;
|
||||
}
|
||||
|
||||
// Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
|
||||
vec3 secondaryFrontColor = v3FrontColor * fKmESun;
|
||||
vec3 frontColor = v3FrontColor * (v3InvWavelength * fKrESun);
|
||||
vec3 v3Direction = v3CameraPos - v3Pos;
|
||||
|
||||
float fCos = dot(v3LightPos, v3Direction) / length(v3Direction);
|
||||
float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);
|
||||
gl_FragColor.rgb = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb;
|
||||
gl_FragColor.a = gl_FragColor.b;
|
||||
}
|
||||
|
|
34
interface/resources/shaders/skin_voxels.vert
Normal file
34
interface/resources/shaders/skin_voxels.vert
Normal file
|
@ -0,0 +1,34 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// skin_voxels.vert
|
||||
// vertex shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/31/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
const int MAX_BONES = 32;
|
||||
const int INDICES_PER_VERTEX = 4;
|
||||
|
||||
uniform mat4 boneMatrices[MAX_BONES];
|
||||
|
||||
attribute vec4 boneIndices;
|
||||
attribute vec4 boneWeights;
|
||||
|
||||
void main(void) {
|
||||
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 normal = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
for (int i = 0; i < INDICES_PER_VERTEX; i++) {
|
||||
mat4 boneMatrix = boneMatrices[int(boneIndices[i])];
|
||||
float boneWeight = boneWeights[i];
|
||||
position += boneMatrix * gl_Vertex * boneWeight;
|
||||
normal += boneMatrix * vec4(gl_Normal, 0.0) * boneWeight;
|
||||
}
|
||||
position = gl_ModelViewProjectionMatrix * position;
|
||||
normal = normalize(gl_ModelViewMatrix * normal);
|
||||
|
||||
gl_FrontColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient +
|
||||
gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position)));
|
||||
gl_Position = position;
|
||||
}
|
|
@ -21,17 +21,26 @@
|
|||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <QActionGroup>
|
||||
#include <QBoxLayout>
|
||||
#include <QColorDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDesktopWidget>
|
||||
#include <QFormLayout>
|
||||
#include <QGLWidget>
|
||||
#include <QKeyEvent>
|
||||
#include <QLineEdit>
|
||||
#include <QMainWindow>
|
||||
#include <QMenuBar>
|
||||
#include <QMouseEvent>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QWheelEvent>
|
||||
#include <QSettings>
|
||||
#include <QShortcut>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <QtDebug>
|
||||
#include <QFileDialog>
|
||||
#include <QDesktopServices>
|
||||
#include <PairingHandler.h>
|
||||
|
||||
#include <AgentTypes.h>
|
||||
|
@ -39,6 +48,7 @@
|
|||
#include <PerfStat.h>
|
||||
#include <AudioInjectionManager.h>
|
||||
#include <AudioInjector.h>
|
||||
#include <OctalCode.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "InterfaceConfig.h"
|
||||
|
@ -50,6 +60,8 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
const bool TESTING_AVATAR_TOUCH = false;
|
||||
|
||||
// Starfield information
|
||||
static char STAR_FILE[] = "https://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";
|
||||
static char STAR_CACHE_FILE[] = "cachedStars.txt";
|
||||
|
@ -156,8 +168,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_window->setWindowTitle("Interface");
|
||||
printLog("Interface Startup:\n");
|
||||
|
||||
_voxels.setViewFrustum(&_viewFrustum);
|
||||
|
||||
unsigned int listenPort = AGENT_SOCKET_LISTEN_PORT;
|
||||
const char** constArgv = const_cast<const char**>(argv);
|
||||
const char* portStr = getCmdOption(argc, constArgv, "--listenPort");
|
||||
|
@ -215,6 +225,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_glWidget->setMouseTracking(true);
|
||||
|
||||
// initialization continues in initializeGL when OpenGL context is ready
|
||||
|
||||
QCoreApplication::setOrganizationDomain("highfidelity.io"); // Used by QSettings on OS X
|
||||
}
|
||||
|
||||
void Application::initializeGL() {
|
||||
|
@ -267,8 +279,6 @@ void Application::initializeGL() {
|
|||
connect(idleTimer, SIGNAL(timeout()), SLOT(idle()));
|
||||
idleTimer->start(0);
|
||||
|
||||
readSettings();
|
||||
|
||||
if (_justStarted) {
|
||||
float startupTime = (usecTimestampNow() - usecTimestamp(&_applicationStartupTime))/1000000.0;
|
||||
_justStarted = false;
|
||||
|
@ -294,15 +304,15 @@ void Application::paintGL() {
|
|||
_myCamera.setUpShift (0.0f);
|
||||
_myCamera.setDistance (0.0f);
|
||||
_myCamera.setTightness (100.0f);
|
||||
_myCamera.setTargetPosition(_myAvatar.getHeadPosition());
|
||||
_myCamera.setTargetPosition(_myAvatar.getHeadJointPosition());
|
||||
_myCamera.setTargetRotation(_myAvatar.getHead().getOrientation());
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||
_myCamera.setTargetPosition(_myAvatar.getSpringyHeadPosition());
|
||||
_myCamera.setTargetPosition(_myAvatar.getBallPosition(AVATAR_JOINT_HEAD_BASE));
|
||||
_myCamera.setTargetRotation(_myAvatar.getHead().getWorldAlignedOrientation());
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setTargetPosition(_myAvatar.getHeadPosition());
|
||||
_myCamera.setTargetPosition(_myAvatar.getHeadJointPosition());
|
||||
_myCamera.setTargetRotation(_myAvatar.getHead().getWorldAlignedOrientation());
|
||||
}
|
||||
|
||||
|
@ -612,6 +622,12 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
resizeGL(_glWidget->width(), _glWidget->height());
|
||||
break;
|
||||
case Qt::Key_Backspace:
|
||||
case Qt::Key_Delete:
|
||||
if (_selectVoxelMode->isChecked()) {
|
||||
deleteVoxelUnderCursor();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
event->ignore();
|
||||
|
@ -724,177 +740,6 @@ void Application::wheelEvent(QWheelEvent* event) {
|
|||
}
|
||||
}
|
||||
|
||||
const char AVATAR_DATA_FILENAME[] = "avatar.ifd";
|
||||
|
||||
void Application::readSettingsFile() {
|
||||
FILE* settingsFile = fopen(AVATAR_DATA_FILENAME, "rt");
|
||||
|
||||
if (settingsFile) {
|
||||
char line[LINE_MAX];
|
||||
|
||||
while (fgets(line, LINE_MAX, settingsFile) != NULL)
|
||||
{
|
||||
if (strcmp(line, " \n") > 0) {
|
||||
char* token = NULL;
|
||||
char* settingLine = NULL;
|
||||
char* toFree = NULL;
|
||||
|
||||
settingLine = strdup(line);
|
||||
|
||||
if (settingLine != NULL) {
|
||||
toFree = settingLine;
|
||||
|
||||
int i = 0;
|
||||
|
||||
char key[128];
|
||||
char value[128];
|
||||
|
||||
|
||||
while ((token = strsep(&settingLine, "=")) != NULL)
|
||||
{
|
||||
switch (i) {
|
||||
case 0:
|
||||
strcpy(key, token);
|
||||
_settingsTable[key] = "";
|
||||
break;
|
||||
|
||||
case 1:
|
||||
strcpy(value, token);
|
||||
_settingsTable[key] = token;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
free(toFree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(settingsFile);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::saveSettingsFile() {
|
||||
FILE* settingsFile = fopen(AVATAR_DATA_FILENAME, "wt");
|
||||
|
||||
if (settingsFile) {
|
||||
for (std::map<std::string, std::string>::iterator i = _settingsTable.begin(); i != _settingsTable.end(); i++)
|
||||
{
|
||||
fprintf(settingsFile, "\n%s=%s", i->first.data(), i->second.data());
|
||||
}
|
||||
}
|
||||
|
||||
fclose(settingsFile);
|
||||
}
|
||||
|
||||
bool Application::getSetting(const char* setting, bool& value, const bool defaultSetting) const {
|
||||
std::map<std::string, std::string>::const_iterator iter = _settingsTable.find(setting);
|
||||
|
||||
if (iter != _settingsTable.end()) {
|
||||
int readBool;
|
||||
|
||||
int res = sscanf(iter->second.data(), "%d", &readBool);
|
||||
|
||||
const char EXPECTED_ITEMS = 1;
|
||||
|
||||
if (res == EXPECTED_ITEMS) {
|
||||
if (readBool == 1) {
|
||||
value = true;
|
||||
} else if (readBool == 0) {
|
||||
value = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Application::getSetting(const char* setting, float& value, const float defaultSetting) const {
|
||||
std::map<std::string, std::string>::const_iterator iter = _settingsTable.find(setting);
|
||||
|
||||
if (iter != _settingsTable.end()) {
|
||||
float readFloat;
|
||||
|
||||
int res = sscanf(iter->second.data(), "%f", &readFloat);
|
||||
|
||||
const char EXPECTED_ITEMS = 1;
|
||||
|
||||
if (res == EXPECTED_ITEMS) {
|
||||
if (!isnan(readFloat)) {
|
||||
value = readFloat;
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Application::getSetting(const char* setting, glm::vec3& value, const glm::vec3& defaultSetting) const {
|
||||
std::map<std::string, std::string>::const_iterator iter = _settingsTable.find(setting);
|
||||
|
||||
if (iter != _settingsTable.end()) {
|
||||
glm::vec3 readVec;
|
||||
|
||||
int res = sscanf(iter->second.data(), "%f,%f,%f", &readVec.x, &readVec.y, &readVec.z);
|
||||
|
||||
const char EXPECTED_ITEMS = 3;
|
||||
|
||||
if (res == EXPECTED_ITEMS) {
|
||||
if (!isnan(readVec.x) && !isnan(readVec.y) && !isnan(readVec.z)) {
|
||||
value = readVec;
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const short MAX_SETTINGS_LENGTH = 128;
|
||||
|
||||
void Application::setSetting(const char* setting, const bool value) {
|
||||
char settingValues[MAX_SETTINGS_LENGTH];
|
||||
sprintf(settingValues, "%d", value);
|
||||
_settingsTable[setting] = settingValues;
|
||||
}
|
||||
|
||||
void Application::setSetting(const char* setting, const float value) {
|
||||
char settingValues[MAX_SETTINGS_LENGTH];
|
||||
sprintf(settingValues, "%f", value);
|
||||
_settingsTable[setting] = settingValues;
|
||||
}
|
||||
|
||||
void Application::setSetting(const char* setting, const glm::vec3& value) {
|
||||
char settingValues[MAX_SETTINGS_LENGTH];
|
||||
sprintf(settingValues, "%f,%f,%f", value.x, value.y, value.z);
|
||||
_settingsTable[setting] = settingValues;
|
||||
}
|
||||
|
||||
// Every second, check the frame rates and other stuff
|
||||
void Application::timer() {
|
||||
gettimeofday(&_timerEnd, NULL);
|
||||
|
@ -1000,7 +845,7 @@ void Application::idle() {
|
|||
_mouseVoxel.z += faceVector.z * _mouseVoxel.s;
|
||||
}
|
||||
}
|
||||
} else if (_addVoxelMode->isChecked()) {
|
||||
} else if (_addVoxelMode->isChecked() || _selectVoxelMode->isChecked()) {
|
||||
// place the voxel a fixed distance away
|
||||
float worldMouseVoxelScale = _mouseVoxelScale * TREE_SCALE;
|
||||
glm::vec3 pt = mouseRayOrigin + mouseRayDirection * (2.0f + worldMouseVoxelScale * 0.5f);
|
||||
|
@ -1014,7 +859,10 @@ void Application::idle() {
|
|||
// red indicates deletion
|
||||
_mouseVoxel.red = 255;
|
||||
_mouseVoxel.green = _mouseVoxel.blue = 0;
|
||||
|
||||
} else if (_selectVoxelMode->isChecked()) {
|
||||
// yellow indicates deletion
|
||||
_mouseVoxel.red = _mouseVoxel.green = 255;
|
||||
_mouseVoxel.blue = 0;
|
||||
} else { // _addVoxelMode->isChecked() || _colorVoxelMode->isChecked()
|
||||
QColor paintColor = _voxelPaintColor->data().value<QColor>();
|
||||
_mouseVoxel.red = paintColor.red();
|
||||
|
@ -1064,6 +912,9 @@ void Application::idle() {
|
|||
for(AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
||||
if (agent->getLinkedData() != NULL) {
|
||||
Avatar *avatar = (Avatar *)agent->getLinkedData();
|
||||
if (!avatar->isInitialized()) {
|
||||
avatar->init();
|
||||
}
|
||||
avatar->simulate(deltaTime, NULL);
|
||||
avatar->setMouseRay(mouseRayOrigin, mouseRayDirection);
|
||||
}
|
||||
|
@ -1078,24 +929,29 @@ void Application::idle() {
|
|||
_myAvatar.simulate(deltaTime, NULL);
|
||||
}
|
||||
|
||||
if (_myCamera.getMode() != CAMERA_MODE_MIRROR && !OculusManager::isConnected()) {
|
||||
if (_manualFirstPerson) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON ) {
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
}
|
||||
} else {
|
||||
|
||||
if (_myAvatar.getIsNearInteractingOther()) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
|
||||
if (TESTING_AVATAR_TOUCH) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
}
|
||||
} else {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_MIRROR && !OculusManager::isConnected()) {
|
||||
if (_manualFirstPerson) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON ) {
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
} else {
|
||||
if (_myAvatar.getIsNearInteractingOther()) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
}
|
||||
} else {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1116,7 +972,10 @@ void Application::terminate() {
|
|||
// Close serial port
|
||||
// close(serial_fd);
|
||||
|
||||
saveSettings();
|
||||
if (_autosave) {
|
||||
saveSettings();
|
||||
_settings->sync();
|
||||
}
|
||||
|
||||
if (_enableNetworkThread) {
|
||||
_stopNetworkReceiveThread = true;
|
||||
|
@ -1124,6 +983,32 @@ void Application::terminate() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::editPreferences() {
|
||||
QDialog dialog(_glWidget);
|
||||
dialog.setWindowTitle("Interface Preferences");
|
||||
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
dialog.setLayout(layout);
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
layout->addLayout(form, 1);
|
||||
|
||||
QLineEdit* avatarURL = new QLineEdit(_settings->value("avatarURL").toString());
|
||||
avatarURL->setMinimumWidth(400);
|
||||
form->addRow("Avatar URL:", avatarURL);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||
layout->addWidget(buttons);
|
||||
|
||||
if (dialog.exec() != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
QUrl url(avatarURL->text());
|
||||
_settings->setValue("avatarURL", url);
|
||||
_myAvatar.getVoxels()->loadVoxelsFromURL(url);
|
||||
}
|
||||
|
||||
void Application::pair() {
|
||||
PairingHandler::sendPairRequest();
|
||||
}
|
||||
|
@ -1235,7 +1120,7 @@ static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail) {
|
|||
void Application::addVoxelInFrontOfAvatar() {
|
||||
VoxelDetail detail;
|
||||
|
||||
glm::vec3 position = (_myAvatar.getPosition() + _myAvatar.getCameraDirection()) * (1.0f / TREE_SCALE);
|
||||
glm::vec3 position = (_myAvatar.getPosition() + _myAvatar.calculateCameraDirection()) * (1.0f / TREE_SCALE);
|
||||
detail.s = _mouseVoxelScale;
|
||||
|
||||
detail.x = detail.s * floor(position.x / detail.s);
|
||||
|
@ -1279,7 +1164,170 @@ void Application::chooseVoxelPaintColor() {
|
|||
// restore the main window's active state
|
||||
_window->activateWindow();
|
||||
}
|
||||
|
||||
const int MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE = 1500;
|
||||
struct SendVoxelsOperationArgs {
|
||||
unsigned char* newBaseOctCode;
|
||||
unsigned char messageBuffer[MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE];
|
||||
int bufferInUse;
|
||||
|
||||
};
|
||||
|
||||
bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
|
||||
SendVoxelsOperationArgs* args = (SendVoxelsOperationArgs*)extraData;
|
||||
if (node->isColored()) {
|
||||
unsigned char* nodeOctalCode = node->getOctalCode();
|
||||
|
||||
unsigned char* codeColorBuffer = NULL;
|
||||
int codeLength = 0;
|
||||
int bytesInCode = 0;
|
||||
int codeAndColorLength;
|
||||
|
||||
// If the newBase is NULL, then don't rebase
|
||||
if (args->newBaseOctCode) {
|
||||
codeColorBuffer = rebaseOctalCode(nodeOctalCode, args->newBaseOctCode, true);
|
||||
codeLength = numberOfThreeBitSectionsInCode(codeColorBuffer);
|
||||
bytesInCode = bytesRequiredForCodeLength(codeLength);
|
||||
codeAndColorLength = bytesInCode + SIZE_OF_COLOR_DATA;
|
||||
} else {
|
||||
codeLength = numberOfThreeBitSectionsInCode(nodeOctalCode);
|
||||
bytesInCode = bytesRequiredForCodeLength(codeLength);
|
||||
codeAndColorLength = bytesInCode + SIZE_OF_COLOR_DATA;
|
||||
codeColorBuffer = new unsigned char[codeAndColorLength];
|
||||
memcpy(codeColorBuffer, nodeOctalCode, bytesInCode);
|
||||
}
|
||||
|
||||
// copy the colors over
|
||||
codeColorBuffer[bytesInCode + RED_INDEX ] = node->getColor()[RED_INDEX ];
|
||||
codeColorBuffer[bytesInCode + GREEN_INDEX] = node->getColor()[GREEN_INDEX];
|
||||
codeColorBuffer[bytesInCode + BLUE_INDEX ] = node->getColor()[BLUE_INDEX ];
|
||||
|
||||
// if we have room don't have room in the buffer, then send the previously generated message first
|
||||
if (args->bufferInUse + codeAndColorLength > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) {
|
||||
AgentList::getInstance()->broadcastToAgents(args->messageBuffer, args->bufferInUse, &AGENT_TYPE_VOXEL, 1);
|
||||
args->bufferInUse = sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int); // reset
|
||||
}
|
||||
|
||||
// copy this node's code color details into our buffer.
|
||||
memcpy(&args->messageBuffer[args->bufferInUse], codeColorBuffer, codeAndColorLength);
|
||||
args->bufferInUse += codeAndColorLength;
|
||||
}
|
||||
return true; // keep going
|
||||
}
|
||||
|
||||
void Application::exportVoxels() {
|
||||
QString desktopLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
|
||||
QString suggestedName = desktopLocation.append("/voxels.svo");
|
||||
|
||||
QString fileNameString = QFileDialog::getSaveFileName(_glWidget, tr("Export Voxels"), suggestedName,
|
||||
tr("Sparse Voxel Octree Files (*.svo)"));
|
||||
QByteArray fileNameAscii = fileNameString.toAscii();
|
||||
const char* fileName = fileNameAscii.data();
|
||||
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
if (selectedNode) {
|
||||
VoxelTree exportTree;
|
||||
_voxels.copySubTreeIntoNewTree(selectedNode, &exportTree, true);
|
||||
exportTree.writeToSVOFile(fileName);
|
||||
}
|
||||
|
||||
// restore the main window's active state
|
||||
_window->activateWindow();
|
||||
}
|
||||
|
||||
void Application::importVoxels() {
|
||||
QString desktopLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
|
||||
QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Import Voxels"), desktopLocation,
|
||||
tr("Sparse Voxel Octree Files (*.svo)"));
|
||||
QByteArray fileNameAscii = fileNameString.toAscii();
|
||||
const char* fileName = fileNameAscii.data();
|
||||
|
||||
// Read the file into a tree
|
||||
VoxelTree importVoxels;
|
||||
importVoxels.readFromSVOFile(fileName);
|
||||
|
||||
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
|
||||
// Recurse the Import Voxels tree, where everything is root relative, and send all the colored voxels to
|
||||
// the server as an set voxel message, this will also rebase the voxels to the new location
|
||||
unsigned char* calculatedOctCode = NULL;
|
||||
SendVoxelsOperationArgs args;
|
||||
args.messageBuffer[0] = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE;
|
||||
unsigned short int* sequenceAt = (unsigned short int*)&args.messageBuffer[sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE)];
|
||||
*sequenceAt = 0;
|
||||
args.bufferInUse = sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int); // set to command + sequence
|
||||
|
||||
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
|
||||
// voxel size/position details.
|
||||
if (selectedNode) {
|
||||
args.newBaseOctCode = selectedNode->getOctalCode();
|
||||
} else {
|
||||
args.newBaseOctCode = calculatedOctCode = pointToVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
}
|
||||
|
||||
importVoxels.recurseTreeWithOperation(sendVoxelsOperation, &args);
|
||||
|
||||
// If we have voxels left in the packet, then send the packet
|
||||
if (args.bufferInUse > (sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int))) {
|
||||
AgentList::getInstance()->broadcastToAgents(args.messageBuffer, args.bufferInUse, &AGENT_TYPE_VOXEL, 1);
|
||||
}
|
||||
|
||||
if (calculatedOctCode) {
|
||||
delete calculatedOctCode;
|
||||
}
|
||||
|
||||
// restore the main window's active state
|
||||
_window->activateWindow();
|
||||
}
|
||||
|
||||
void Application::cutVoxels() {
|
||||
copyVoxels();
|
||||
deleteVoxelUnderCursor();
|
||||
}
|
||||
|
||||
void Application::copyVoxels() {
|
||||
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
if (selectedNode) {
|
||||
// clear the clipboard first...
|
||||
_clipboardTree.eraseAllVoxels();
|
||||
|
||||
// then copy onto it
|
||||
_voxels.copySubTreeIntoNewTree(selectedNode, &_clipboardTree, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::pasteVoxels() {
|
||||
unsigned char* calculatedOctCode = NULL;
|
||||
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
|
||||
// Recurse the clipboard tree, where everything is root relative, and send all the colored voxels to
|
||||
// the server as an set voxel message, this will also rebase the voxels to the new location
|
||||
SendVoxelsOperationArgs args;
|
||||
args.messageBuffer[0] = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE;
|
||||
unsigned short int* sequenceAt = (unsigned short int*)&args.messageBuffer[sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE)];
|
||||
*sequenceAt = 0;
|
||||
args.bufferInUse = sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int); // set to command + sequence
|
||||
|
||||
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
|
||||
// voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a
|
||||
// target octalCode for where the user is pointing.
|
||||
if (selectedNode) {
|
||||
args.newBaseOctCode = selectedNode->getOctalCode();
|
||||
} else {
|
||||
args.newBaseOctCode = calculatedOctCode = pointToVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
}
|
||||
|
||||
_clipboardTree.recurseTreeWithOperation(sendVoxelsOperation, &args);
|
||||
|
||||
// If we have voxels left in the packet, then send the packet
|
||||
if (args.bufferInUse > (sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int))) {
|
||||
AgentList::getInstance()->broadcastToAgents(args.messageBuffer, args.bufferInUse, &AGENT_TYPE_VOXEL, 1);
|
||||
}
|
||||
|
||||
if (calculatedOctCode) {
|
||||
delete calculatedOctCode;
|
||||
}
|
||||
}
|
||||
|
||||
void Application::initMenu() {
|
||||
QMenuBar* menuBar = new QMenuBar();
|
||||
_window->setMenuBar(menuBar);
|
||||
|
@ -1287,6 +1335,9 @@ void Application::initMenu() {
|
|||
QMenu* fileMenu = menuBar->addMenu("File");
|
||||
fileMenu->addAction("Quit", this, SLOT(quit()), Qt::CTRL | Qt::Key_Q);
|
||||
|
||||
QMenu* editMenu = menuBar->addMenu("Edit");
|
||||
editMenu->addAction("Preferences...", this, SLOT(editPreferences()));
|
||||
|
||||
QMenu* pairMenu = menuBar->addMenu("Pair");
|
||||
pairMenu->addAction("Pair", this, SLOT(pair()));
|
||||
|
||||
|
@ -1327,9 +1378,7 @@ void Application::initMenu() {
|
|||
renderMenu->addAction("First Person", this, SLOT(setRenderFirstPerson(bool)), Qt::Key_P)->setCheckable(true);
|
||||
|
||||
QMenu* toolsMenu = menuBar->addMenu("Tools");
|
||||
|
||||
(_renderStatsOn = toolsMenu->addAction("Stats"))->setCheckable(true);
|
||||
|
||||
_renderStatsOn->setShortcut(Qt::Key_Slash);
|
||||
(_logOn = toolsMenu->addAction("Log"))->setCheckable(true);
|
||||
_logOn->setChecked(false);
|
||||
|
@ -1338,26 +1387,40 @@ void Application::initMenu() {
|
|||
QMenu* voxelMenu = menuBar->addMenu("Voxels");
|
||||
_voxelModeActions = new QActionGroup(this);
|
||||
_voxelModeActions->setExclusive(false); // exclusivity implies one is always checked
|
||||
|
||||
(_addVoxelMode = voxelMenu->addAction(
|
||||
"Add Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_1))->setCheckable(true);
|
||||
"Add Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_A))->setCheckable(true);
|
||||
_voxelModeActions->addAction(_addVoxelMode);
|
||||
(_deleteVoxelMode = voxelMenu->addAction(
|
||||
"Delete Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_2))->setCheckable(true);
|
||||
"Delete Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_D))->setCheckable(true);
|
||||
_voxelModeActions->addAction(_deleteVoxelMode);
|
||||
(_colorVoxelMode = voxelMenu->addAction(
|
||||
"Color Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_3))->setCheckable(true);
|
||||
"Color Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_B))->setCheckable(true);
|
||||
_voxelModeActions->addAction(_colorVoxelMode);
|
||||
(_selectVoxelMode = voxelMenu->addAction(
|
||||
"Select Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_S))->setCheckable(true);
|
||||
_voxelModeActions->addAction(_selectVoxelMode);
|
||||
(_eyedropperMode = voxelMenu->addAction(
|
||||
"Get Color Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_G))->setCheckable(true);
|
||||
_voxelModeActions->addAction(_eyedropperMode);
|
||||
|
||||
voxelMenu->addAction("Place Voxel", this, SLOT(addVoxelInFrontOfAvatar()), Qt::Key_4);
|
||||
voxelMenu->addAction("Decrease Voxel Size", this, SLOT(decreaseVoxelSize()), Qt::Key_5);
|
||||
voxelMenu->addAction("Increase Voxel Size", this, SLOT(increaseVoxelSize()), Qt::Key_6);
|
||||
voxelMenu->addAction("Place New Voxel", this, SLOT(addVoxelInFrontOfAvatar()), Qt::CTRL | Qt::Key_N);
|
||||
voxelMenu->addAction("Decrease Voxel Size", this, SLOT(decreaseVoxelSize()), QKeySequence::ZoomOut);
|
||||
voxelMenu->addAction("Increase Voxel Size", this, SLOT(increaseVoxelSize()), QKeySequence::ZoomIn);
|
||||
|
||||
_voxelPaintColor = voxelMenu->addAction("Voxel Paint Color", this, SLOT(chooseVoxelPaintColor()), Qt::Key_7);
|
||||
_voxelPaintColor = voxelMenu->addAction("Voxel Paint Color", this,
|
||||
SLOT(chooseVoxelPaintColor()), Qt::META | Qt::Key_C);
|
||||
QColor paintColor(128, 128, 128);
|
||||
_voxelPaintColor->setData(paintColor);
|
||||
_voxelPaintColor->setIcon(createSwatchIcon(paintColor));
|
||||
(_destructiveAddVoxel = voxelMenu->addAction("Create Voxel is Destructive"))->setCheckable(true);
|
||||
|
||||
voxelMenu->addAction("Export Voxels", this, SLOT(exportVoxels()), Qt::CTRL | Qt::Key_E);
|
||||
voxelMenu->addAction("Import Voxels", this, SLOT(importVoxels()), Qt::CTRL | Qt::Key_I);
|
||||
voxelMenu->addAction("Cut Voxels", this, SLOT(cutVoxels()), Qt::CTRL | Qt::Key_X);
|
||||
voxelMenu->addAction("Copy Voxels", this, SLOT(copyVoxels()), Qt::CTRL | Qt::Key_C);
|
||||
voxelMenu->addAction("Paste Voxels", this, SLOT(pasteVoxels()), Qt::CTRL | Qt::Key_V);
|
||||
|
||||
QMenu* frustumMenu = menuBar->addMenu("Frustum");
|
||||
(_frustumOn = frustumMenu->addAction("Display Frustum"))->setCheckable(true);
|
||||
_frustumOn->setShortcut(Qt::SHIFT | Qt::Key_F);
|
||||
|
@ -1383,6 +1446,17 @@ void Application::initMenu() {
|
|||
debugMenu->addAction("Wants Res-In", this, SLOT(setWantsResIn(bool)))->setCheckable(true);
|
||||
debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true);
|
||||
debugMenu->addAction("Wants View Delta Sending", this, SLOT(setWantsDelta(bool)))->setCheckable(true);
|
||||
|
||||
QMenu* settingsMenu = menuBar->addMenu("Settings");
|
||||
(_settingsAutosave = settingsMenu->addAction("Autosave", this, SLOT(setAutosave(bool))))->setCheckable(true);
|
||||
_settingsAutosave->setChecked(true);
|
||||
settingsMenu->addAction("Load settings", this, SLOT(loadSettings()));
|
||||
settingsMenu->addAction("Save settings", this, SLOT(saveSettings()));
|
||||
settingsMenu->addAction("Import settings", this, SLOT(importSettings()));
|
||||
settingsMenu->addAction("Export settings", this, SLOT(exportSettings()));
|
||||
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
_settings = new QSettings("High Fidelity", "Interface", this);
|
||||
}
|
||||
|
||||
void Application::updateFrustumRenderModeAction() {
|
||||
|
@ -1417,8 +1491,6 @@ void Application::initDisplay() {
|
|||
|
||||
void Application::init() {
|
||||
_voxels.init();
|
||||
_voxels.setViewerAvatar(&_myAvatar);
|
||||
_voxels.setCamera(&_myCamera);
|
||||
|
||||
_environment.init();
|
||||
|
||||
|
@ -1429,11 +1501,14 @@ void Application::init() {
|
|||
|
||||
_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0);
|
||||
|
||||
_myAvatar.init();
|
||||
_myAvatar.setPosition(START_LOCATION);
|
||||
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON );
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
_myAvatar.setDisplayingLookatVectors(false);
|
||||
|
||||
_myAvatar.getVoxels()->loadVoxelsFromURL(_settings->value("avatarURL").toUrl());
|
||||
|
||||
QCursor::setPos(_headMouseX, _headMouseY);
|
||||
|
||||
OculusManager::connect();
|
||||
|
@ -1443,6 +1518,8 @@ void Application::init() {
|
|||
|
||||
gettimeofday(&_timerStart, NULL);
|
||||
gettimeofday(&_lastTimeIdle, NULL);
|
||||
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
void Application::updateAvatar(float deltaTime) {
|
||||
|
@ -1489,9 +1566,7 @@ void Application::updateAvatar(float deltaTime) {
|
|||
// to the server.
|
||||
loadViewFrustum(_myCamera, _viewFrustum);
|
||||
_myAvatar.setCameraPosition(_viewFrustum.getPosition());
|
||||
_myAvatar.setCameraDirection(_viewFrustum.getDirection());
|
||||
_myAvatar.setCameraUp(_viewFrustum.getUp());
|
||||
_myAvatar.setCameraRight(_viewFrustum.getRight());
|
||||
_myAvatar.setCameraOrientation(_viewFrustum.getOrientation());
|
||||
_myAvatar.setCameraFov(_viewFrustum.getFieldOfView());
|
||||
_myAvatar.setCameraAspectRatio(_viewFrustum.getAspectRatio());
|
||||
_myAvatar.setCameraNearClip(_viewFrustum.getNearClip());
|
||||
|
@ -1547,7 +1622,7 @@ void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
|
|||
if (_cameraFrustum->isChecked()) {
|
||||
position = camera.getPosition();
|
||||
} else {
|
||||
position = _myAvatar.getHeadPosition();
|
||||
position = _myAvatar.getHeadJointPosition();
|
||||
}
|
||||
|
||||
float fov = camera.getFieldOfView();
|
||||
|
@ -1555,24 +1630,10 @@ void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
|
|||
float farClip = camera.getFarClip();
|
||||
|
||||
glm::quat rotation = camera.getRotation();
|
||||
glm::vec3 direction = rotation * AVATAR_FRONT;
|
||||
glm::vec3 up = rotation * AVATAR_UP;
|
||||
glm::vec3 right = rotation * AVATAR_RIGHT;
|
||||
|
||||
/*
|
||||
printf("position.x=%f, position.y=%f, position.z=%f\n", position.x, position.y, position.z);
|
||||
printf("yaw=%f, pitch=%f, roll=%f\n", yaw,pitch,roll);
|
||||
printf("direction.x=%f, direction.y=%f, direction.z=%f\n", direction.x, direction.y, direction.z);
|
||||
printf("up.x=%f, up.y=%f, up.z=%f\n", up.x, up.y, up.z);
|
||||
printf("right.x=%f, right.y=%f, right.z=%f\n", right.x, right.y, right.z);
|
||||
printf("fov=%f\n", fov);
|
||||
printf("nearClip=%f\n", nearClip);
|
||||
printf("farClip=%f\n", farClip);
|
||||
*/
|
||||
|
||||
// Set the viewFrustum up with the correct position and orientation of the camera
|
||||
viewFrustum.setPosition(position);
|
||||
viewFrustum.setOrientation(direction,up,right);
|
||||
viewFrustum.setOrientation(rotation);
|
||||
|
||||
// Also make sure it's got the correct lens details from the camera
|
||||
viewFrustum.setFieldOfView(fov);
|
||||
|
@ -1730,7 +1791,7 @@ void Application::displayOculus(Camera& whichCamera) {
|
|||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
||||
void Application::displaySide(Camera& whichCamera) {
|
||||
// transform by eye offset
|
||||
|
||||
|
@ -1840,6 +1901,9 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
||||
if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
|
||||
Avatar *avatar = (Avatar *)agent->getLinkedData();
|
||||
if (!avatar->isInitialized()) {
|
||||
avatar->init();
|
||||
}
|
||||
avatar->render(false);
|
||||
}
|
||||
}
|
||||
|
@ -2168,9 +2232,8 @@ void Application::maybeEditVoxelUnderCursor() {
|
|||
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(11025);
|
||||
voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z));
|
||||
//_myAvatar.getPosition()
|
||||
voxelInjector->setBearing(-1 * _myAvatar.getAbsoluteHeadYaw());
|
||||
// voxelInjector->setBearing(-1 * _myAvatar.getAbsoluteHeadYaw());
|
||||
voxelInjector->setVolume (16 * pow (_mouseVoxel.s, 2) / .0000001); //255 is max, and also default value
|
||||
// printf("mousevoxelscale is %f\n", _mouseVoxel.s);
|
||||
|
||||
/* for (int i = 0; i
|
||||
< 22050; i++) {
|
||||
|
@ -2221,6 +2284,8 @@ void Application::maybeEditVoxelUnderCursor() {
|
|||
}
|
||||
} else if (_deleteVoxelMode->isChecked()) {
|
||||
deleteVoxelUnderCursor();
|
||||
} else if (_eyedropperMode->isChecked()) {
|
||||
eyedropperVoxelUnderCursor();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2230,13 +2295,13 @@ void Application::deleteVoxelUnderCursor() {
|
|||
sendVoxelEditMessage(PACKET_HEADER_ERASE_VOXEL, _mouseVoxel);
|
||||
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(5000);
|
||||
voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z));
|
||||
voxelInjector->setBearing(0); //straight down the z axis
|
||||
// voxelInjector->setBearing(0); //straight down the z axis
|
||||
voxelInjector->setVolume (255); //255 is max, and also default value
|
||||
|
||||
|
||||
for (int i = 0; i < 5000; i++) {
|
||||
voxelInjector->addSample(10000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 500.0)))); //FM 3 resonant pulse
|
||||
// voxelInjector->addSample(20000 * sin((i) /((4 / _mouseVoxel.s) * sin((i)/(20 * _mouseVoxel.s / .001))))); //FM 2 comb filter
|
||||
//voxelInjector->addSample(20000 * sin((i) /((4 / _mouseVoxel.s) * sin((i)/(20 * _mouseVoxel.s / .001))))); //FM 2 comb filter
|
||||
}
|
||||
|
||||
AudioInjectionManager::threadInjector(voxelInjector);
|
||||
|
@ -2245,6 +2310,20 @@ void Application::deleteVoxelUnderCursor() {
|
|||
_justEditedVoxel = true;
|
||||
}
|
||||
|
||||
void Application::eyedropperVoxelUnderCursor() {
|
||||
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
if (selectedNode && selectedNode->isColored()) {
|
||||
QColor selectedColor(selectedNode->getColor()[RED_INDEX],
|
||||
selectedNode->getColor()[GREEN_INDEX],
|
||||
selectedNode->getColor()[BLUE_INDEX]);
|
||||
|
||||
if (selectedColor.isValid()) {
|
||||
_voxelPaintColor->setData(selectedColor);
|
||||
_voxelPaintColor->setIcon(createSwatchIcon(selectedColor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::goHome() {
|
||||
_myAvatar.setPosition(START_LOCATION);
|
||||
}
|
||||
|
@ -2357,95 +2436,82 @@ void* Application::networkReceive(void* args) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void Application::saveSettings()
|
||||
{
|
||||
// Handle any persistent settings saving here when we get a call to terminate.
|
||||
// This should probably be moved to a map stored in memory at some point to cache settings.
|
||||
_myAvatar.writeAvatarDataToFile();
|
||||
|
||||
setSetting("_gyroLook", _gyroLook->isChecked());
|
||||
|
||||
setSetting("_mouseLook", _mouseLook->isChecked());
|
||||
|
||||
setSetting("_transmitterDrives", _transmitterDrives->isChecked());
|
||||
|
||||
setSetting("_renderVoxels", _renderVoxels->isChecked());
|
||||
|
||||
setSetting("_renderVoxelTextures", _renderVoxelTextures->isChecked());
|
||||
|
||||
setSetting("_renderStarsOn", _renderStarsOn->isChecked());
|
||||
|
||||
setSetting("_renderAtmosphereOn", _renderAtmosphereOn->isChecked());
|
||||
|
||||
setSetting("_renderAvatarsOn", _renderAvatarsOn->isChecked());
|
||||
|
||||
setSetting("_renderStatsOn", _renderStatsOn->isChecked());
|
||||
|
||||
setSetting("_renderFrameTimerOn", _renderFrameTimerOn->isChecked());
|
||||
|
||||
setSetting("_renderLookatOn", _renderLookatOn->isChecked());
|
||||
|
||||
setSetting("_logOn", _logOn->isChecked());
|
||||
|
||||
setSetting("_frustumOn", _frustumOn->isChecked());
|
||||
|
||||
setSetting("_viewFrustumFromOffset", _viewFrustumFromOffset->isChecked());
|
||||
|
||||
setSetting("_cameraFrustum", _cameraFrustum->isChecked());
|
||||
|
||||
saveSettingsFile();
|
||||
void Application::scanMenuBar(settingsAction modifySetting, QSettings* set) {
|
||||
if (!_window->menuBar()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QList<QMenu*> menus = _window->menuBar()->findChildren<QMenu *>();
|
||||
|
||||
for (QList<QMenu *>::const_iterator it = menus.begin(); menus.end() != it; ++it) {
|
||||
scanMenu(*it, modifySetting, set);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::readSettings()
|
||||
{
|
||||
readSettingsFile();
|
||||
_myAvatar.readAvatarDataFromFile();
|
||||
|
||||
bool settingState;
|
||||
getSetting("_gyroLook", settingState, _gyroLook->isChecked());
|
||||
_gyroLook->setChecked(settingState);
|
||||
|
||||
getSetting("_mouseLook", settingState, _mouseLook->isChecked());
|
||||
_mouseLook->setChecked(settingState);
|
||||
|
||||
getSetting("_transmitterDrives", settingState, _transmitterDrives->isChecked());
|
||||
_transmitterDrives->setChecked(settingState);
|
||||
|
||||
getSetting("_renderVoxels", settingState, _renderVoxels->isChecked());
|
||||
_renderVoxels->setChecked(settingState);
|
||||
|
||||
getSetting("_renderVoxelTextures", settingState, _renderVoxelTextures->isChecked());
|
||||
_renderVoxelTextures->setChecked(settingState);
|
||||
|
||||
getSetting("_renderStarsOn", settingState, _renderStarsOn->isChecked());
|
||||
_renderStarsOn->setChecked(settingState);
|
||||
|
||||
getSetting("_renderAtmosphereOn", settingState, _renderAtmosphereOn->isChecked());
|
||||
_renderAtmosphereOn->setChecked(settingState);
|
||||
|
||||
getSetting("_renderAvatarsOn", settingState, _renderAvatarsOn->isChecked());
|
||||
_renderAvatarsOn->setChecked(settingState);
|
||||
|
||||
getSetting("_renderStatsOn", settingState, _renderStatsOn->isChecked());
|
||||
_renderStatsOn->setChecked(settingState);
|
||||
|
||||
getSetting("_renderFrameTimerOn", settingState, _renderFrameTimerOn->isChecked());
|
||||
_renderFrameTimerOn->setChecked(settingState);
|
||||
|
||||
getSetting("_renderLookatOn", settingState, _renderLookatOn->isChecked());
|
||||
_renderLookatOn->setChecked(settingState);
|
||||
|
||||
getSetting("_logOn", settingState, _logOn->isChecked());
|
||||
_logOn->setChecked(settingState);
|
||||
|
||||
getSetting("_frustumOn", settingState, _frustumOn->isChecked());
|
||||
_frustumOn->setChecked(settingState);
|
||||
|
||||
getSetting("_viewFrustumFromOffset", settingState, _viewFrustumFromOffset->isChecked());
|
||||
_viewFrustumFromOffset->setChecked(settingState);
|
||||
|
||||
getSetting("_cameraFrustum", settingState, _cameraFrustum->isChecked());
|
||||
_cameraFrustum->setChecked(settingState);
|
||||
|
||||
void Application::scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set) {
|
||||
QList<QAction*> actions = menu->actions();
|
||||
|
||||
set->beginGroup(menu->title());
|
||||
for (QList<QAction *>::const_iterator it = actions.begin(); actions.end() != it; ++it) {
|
||||
if ((*it)->menu()) {
|
||||
scanMenu((*it)->menu(), modifySetting, set);
|
||||
}
|
||||
if ((*it)->isCheckable()) {
|
||||
modifySetting(set, *it);
|
||||
}
|
||||
}
|
||||
set->endGroup();
|
||||
}
|
||||
|
||||
void Application::loadAction(QSettings* set, QAction* action) {
|
||||
action->setChecked(set->value(action->text(), action->isChecked()).toBool());
|
||||
}
|
||||
|
||||
void Application::saveAction(QSettings* set, QAction* action) {
|
||||
set->setValue(action->text(), action->isChecked());
|
||||
}
|
||||
|
||||
void Application::setAutosave(bool wantsAutosave) {
|
||||
_autosave = wantsAutosave;
|
||||
}
|
||||
|
||||
void Application::loadSettings(QSettings* set) {
|
||||
if (!set) set = getSettings();
|
||||
|
||||
scanMenuBar(&Application::loadAction, set);
|
||||
getAvatar()->loadData(set);
|
||||
}
|
||||
|
||||
void Application::saveSettings(QSettings* set) {
|
||||
if (!set) set = getSettings();
|
||||
|
||||
scanMenuBar(&Application::saveAction, set);
|
||||
getAvatar()->saveData(set);
|
||||
}
|
||||
|
||||
void Application::importSettings() {
|
||||
QString locationDir(QDesktopServices::displayName(QDesktopServices::DesktopLocation));
|
||||
QString fileName = QFileDialog::getOpenFileName(_window,
|
||||
tr("Open .ini config file"),
|
||||
locationDir,
|
||||
tr("Text files (*.ini)"));
|
||||
if (fileName != "") {
|
||||
QSettings tmp(fileName, QSettings::IniFormat);
|
||||
loadSettings(&tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::exportSettings() {
|
||||
QString locationDir(QDesktopServices::displayName(QDesktopServices::DesktopLocation));
|
||||
QString fileName = QFileDialog::getSaveFileName(_window,
|
||||
tr("Save .ini config file"),
|
||||
locationDir,
|
||||
tr("Text files (*.ini)"));
|
||||
if (fileName != "") {
|
||||
QSettings tmp(fileName, QSettings::IniFormat);
|
||||
saveSettings(&tmp);
|
||||
tmp.sync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#include <QApplication>
|
||||
#include <QAction>
|
||||
#include <QSettings>
|
||||
#include <QList>
|
||||
|
||||
#include <AgentList.h>
|
||||
|
||||
|
@ -37,6 +39,8 @@ class QGLWidget;
|
|||
class QKeyEvent;
|
||||
class QMainWindow;
|
||||
class QMouseEvent;
|
||||
class QNetworkAccessManager;
|
||||
class QSettings;
|
||||
class QWheelEvent;
|
||||
|
||||
class Agent;
|
||||
|
@ -65,67 +69,22 @@ public:
|
|||
|
||||
Avatar* getAvatar() { return &_myAvatar; }
|
||||
Camera* getCamera() { return &_myCamera; }
|
||||
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
|
||||
VoxelSystem* getVoxels() { return &_voxels; }
|
||||
QSettings* getSettings() { return _settings; }
|
||||
Environment* getEnvironment() { return &_environment; }
|
||||
bool shouldEchoAudio() { return _echoAudioMode->isChecked(); }
|
||||
|
||||
/*!
|
||||
@fn getSettingBool
|
||||
@brief A function for getting boolean settings from the settings file.
|
||||
@param settingName The desired setting to get the value for.
|
||||
@param boolSetting The referenced variable where the setting will be stored.
|
||||
@param defaultSetting The default setting to assign to boolSetting if this function fails to find the appropriate setting. Defaults to false.
|
||||
*/
|
||||
bool getSetting(const char* setting, bool &value, const bool defaultSetting = false) const;
|
||||
QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
|
||||
|
||||
/*!
|
||||
@fn getSettingFloat
|
||||
@brief A function for getting float settings from the settings file.
|
||||
@param settingName The desired setting to get the value for.
|
||||
@param floatSetting The referenced variable where the setting will be stored.
|
||||
@param defaultSetting The default setting to assign to boolSetting if this function fails to find the appropriate setting. Defaults to 0.0f.
|
||||
*/
|
||||
bool getSetting(const char* setting, float &value, const float defaultSetting = 0.0f) const;
|
||||
|
||||
/*!
|
||||
@fn getSettingVec3
|
||||
@brief A function for getting boolean settings from the settings file.
|
||||
@param settingName The desired setting to get the value for.
|
||||
@param vecSetting The referenced variable where the setting will be stored.
|
||||
@param defaultSetting The default setting to assign to boolSetting if this function fails to find the appropriate setting. Defaults to <0.0f, 0.0f, 0.0f>
|
||||
*/
|
||||
bool getSetting(const char* setting, glm::vec3 &value, const glm::vec3& defaultSetting = glm::vec3(0.0f, 0.0f, 0.0f)) const;
|
||||
|
||||
/*!
|
||||
@fn setSettingBool
|
||||
@brief A function for setting boolean setting values when saving the settings file.
|
||||
@param settingName The desired setting to populate a value for.
|
||||
@param boolSetting The value to set.
|
||||
*/
|
||||
void setSetting(const char* setting, const bool value);
|
||||
|
||||
/*!
|
||||
@fn setSettingFloat
|
||||
@brief A function for setting boolean setting values when saving the settings file.
|
||||
@param settingName The desired setting to populate a value for.
|
||||
@param floatSetting The value to set.
|
||||
*/
|
||||
void setSetting(const char* setting, const float value);
|
||||
|
||||
/*!
|
||||
@fn setSettingVec3
|
||||
@brief A function for setting boolean setting values when saving the settings file.
|
||||
@param settingName The desired setting to populate a value for.
|
||||
@param vecSetting The value to set.
|
||||
*/
|
||||
void setSetting(const char* setting, const glm::vec3& value);
|
||||
|
||||
private slots:
|
||||
|
||||
void timer();
|
||||
void idle();
|
||||
void terminate();
|
||||
|
||||
void editPreferences();
|
||||
|
||||
void pair();
|
||||
|
||||
void setHead(bool head);
|
||||
|
@ -154,8 +113,19 @@ private slots:
|
|||
void decreaseVoxelSize();
|
||||
void increaseVoxelSize();
|
||||
void chooseVoxelPaintColor();
|
||||
|
||||
void setAutosave(bool wantsAutosave);
|
||||
void loadSettings(QSettings* set = NULL);
|
||||
void saveSettings(QSettings* set = NULL);
|
||||
void importSettings();
|
||||
void exportSettings();
|
||||
void exportVoxels();
|
||||
void importVoxels();
|
||||
void cutVoxels();
|
||||
void copyVoxels();
|
||||
void pasteVoxels();
|
||||
private:
|
||||
|
||||
static bool sendVoxelsOperation(VoxelNode* node, void* extraData);
|
||||
|
||||
void initMenu();
|
||||
void updateFrustumRenderModeAction();
|
||||
|
@ -176,7 +146,7 @@ private:
|
|||
void shiftPaintingColor();
|
||||
void maybeEditVoxelUnderCursor();
|
||||
void deleteVoxelUnderCursor();
|
||||
|
||||
void eyedropperVoxelUnderCursor();
|
||||
void goHome();
|
||||
void resetSensors();
|
||||
|
||||
|
@ -189,13 +159,13 @@ private:
|
|||
static void attachNewHeadToAgent(Agent *newAgent);
|
||||
static void* networkReceive(void* args);
|
||||
|
||||
// These two functions are technically not necessary, but they help keep things in one place.
|
||||
void readSettings(); //! This function is largely to help consolidate getting settings in one place.
|
||||
void saveSettings(); //! This function is to consolidate any settings setting in one place.
|
||||
|
||||
void readSettingsFile(); //! This function reads data from the settings file, splitting data into key value pairs using '=' as a delimiter.
|
||||
void saveSettingsFile(); //! This function writes all changes in the settings table to the settings file, serializing all settings added through the setSetting functions.
|
||||
|
||||
// methodes handling menu settings
|
||||
typedef void(*settingsAction)(QSettings*, QAction*);
|
||||
static void loadAction(QSettings* set, QAction* action);
|
||||
static void saveAction(QSettings* set, QAction* action);
|
||||
void scanMenuBar(settingsAction modifySetting, QSettings* set);
|
||||
void scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set);
|
||||
|
||||
QMainWindow* _window;
|
||||
QGLWidget* _glWidget;
|
||||
|
||||
|
@ -218,6 +188,8 @@ private:
|
|||
QAction* _addVoxelMode; // Whether add voxel mode is enabled
|
||||
QAction* _deleteVoxelMode; // Whether delete voxel mode is enabled
|
||||
QAction* _colorVoxelMode; // Whether color voxel mode is enabled
|
||||
QAction* _selectVoxelMode; // Whether select voxel mode is enabled
|
||||
QAction* _eyedropperMode; // Whether voxel color eyedropper mode is enabled
|
||||
QAction* _voxelPaintColor; // The color with which to paint voxels
|
||||
QAction* _destructiveAddVoxel; // when doing voxel editing do we want them to be destructive
|
||||
QAction* _frustumOn; // Whether or not to display the debug view frustum
|
||||
|
@ -225,6 +197,9 @@ private:
|
|||
QAction* _cameraFrustum; // which frustum to look at
|
||||
QAction* _fullScreenMode; // whether we are in full screen mode
|
||||
QAction* _frustumRenderModeAction;
|
||||
QAction* _settingsAutosave; // Whether settings are saved automatically
|
||||
|
||||
QNetworkAccessManager* _networkAccessManager;
|
||||
|
||||
SerialInterface _serialPort;
|
||||
bool _displayLevels;
|
||||
|
@ -242,6 +217,8 @@ private:
|
|||
Stars _stars;
|
||||
|
||||
VoxelSystem _voxels;
|
||||
VoxelTree _clipboardTree; // if I copy/paste
|
||||
|
||||
QByteArray _voxelsFilename;
|
||||
bool _wantToKillLocalVoxels;
|
||||
|
||||
|
@ -315,11 +292,8 @@ private:
|
|||
int _bytesPerSecond;
|
||||
int _bytesCount;
|
||||
|
||||
/*!
|
||||
* Store settings in a map, storing keys and values as strings.
|
||||
* Interpret values as needed on demand. through the appropriate getters and setters.
|
||||
*/
|
||||
std::map<std::string, std::string> _settingsTable;
|
||||
QSettings* _settings; // Contain Menu settings and Avatar data
|
||||
bool _autosave; // True if the autosave is on.
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__Application__) */
|
||||
|
|
|
@ -116,7 +116,7 @@ int audioCallback (const void* inputBuffer,
|
|||
printLog("got output\n");
|
||||
}
|
||||
|
||||
if (inputLeft) {
|
||||
if (agentList && inputLeft) {
|
||||
|
||||
// Measure the loudness of the signal from the microphone and store in audio object
|
||||
float loudness = 0;
|
||||
|
@ -133,7 +133,10 @@ int audioCallback (const void* inputBuffer,
|
|||
Agent* audioMixer = agentList->soloAgentOfType(AGENT_TYPE_AUDIO_MIXER);
|
||||
|
||||
if (audioMixer) {
|
||||
int leadingBytes = 2 + (sizeof(float) * 4);
|
||||
glm::vec3 headPosition = interfaceAvatar->getHeadJointPosition();
|
||||
glm::quat headOrientation = interfaceAvatar->getHead().getOrientation();
|
||||
|
||||
int leadingBytes = 1 + sizeof(headPosition) + sizeof(headOrientation) + sizeof(unsigned char);
|
||||
|
||||
// we need the amount of bytes in the buffer + 1 for type
|
||||
// + 12 for 3 floats for position + float for bearing + 1 attenuation byte
|
||||
|
@ -143,29 +146,15 @@ int audioCallback (const void* inputBuffer,
|
|||
unsigned char *currentPacketPtr = dataPacket + 1;
|
||||
|
||||
// memcpy the three float positions
|
||||
memcpy(currentPacketPtr, &interfaceAvatar->getHeadPosition(), sizeof(float) * 3);
|
||||
currentPacketPtr += (sizeof(float) * 3);
|
||||
memcpy(currentPacketPtr, &headPosition, sizeof(headPosition));
|
||||
currentPacketPtr += (sizeof(headPosition));
|
||||
|
||||
// tell the mixer not to add additional attenuation to our source
|
||||
*(currentPacketPtr++) = 255;
|
||||
|
||||
// memcpy the corrected render yaw
|
||||
float correctedYaw = fmodf(-1 * interfaceAvatar->getAbsoluteHeadYaw(), 360);
|
||||
|
||||
if (correctedYaw > 180) {
|
||||
correctedYaw -= 360;
|
||||
} else if (correctedYaw < -180) {
|
||||
correctedYaw += 360;
|
||||
}
|
||||
|
||||
if (Application::getInstance()->shouldEchoAudio()) {
|
||||
correctedYaw = correctedYaw > 0
|
||||
? correctedYaw + AGENT_LOOPBACK_MODIFIER
|
||||
: correctedYaw - AGENT_LOOPBACK_MODIFIER;
|
||||
}
|
||||
|
||||
memcpy(currentPacketPtr, &correctedYaw, sizeof(float));
|
||||
currentPacketPtr += sizeof(float);
|
||||
// memcpy our orientation
|
||||
memcpy(currentPacketPtr, &headOrientation, sizeof(headOrientation));
|
||||
currentPacketPtr += sizeof(headOrientation);
|
||||
|
||||
// copy the audio data to the last BUFFER_LENGTH_BYTES bytes of the data packet
|
||||
memcpy(currentPacketPtr, inputLeft, BUFFER_LENGTH_BYTES);
|
||||
|
|
|
@ -44,7 +44,7 @@ const float HEAD_MAX_YAW = 85;
|
|||
const float HEAD_MIN_YAW = -85;
|
||||
const float PERIPERSONAL_RADIUS = 1.0f;
|
||||
const float AVATAR_BRAKING_STRENGTH = 40.0f;
|
||||
const float JOINT_TOUCH_RANGE = 0.01f;
|
||||
const float MOUSE_RAY_TOUCH_RANGE = 0.01f;
|
||||
const float FLOATING_HEIGHT = 0.13f;
|
||||
const bool USING_HEAD_LEAN = false;
|
||||
const float LEAN_SENSITIVITY = 0.15;
|
||||
|
@ -55,7 +55,6 @@ const float SKIN_COLOR[] = {1.0, 0.84, 0.66};
|
|||
const float DARK_SKIN_COLOR[] = {0.9, 0.78, 0.63};
|
||||
const int NUM_BODY_CONE_SIDES = 9;
|
||||
|
||||
|
||||
bool usingBigSphereCollisionTest = true;
|
||||
|
||||
float chatMessageScale = 0.0015;
|
||||
|
@ -101,7 +100,7 @@ _isMouseTurningRight(false)
|
|||
|
||||
initializeBodyBalls();
|
||||
|
||||
_height = _skeleton.getHeight() + _bodyBall[ AVATAR_JOINT_LEFT_HEEL ].radius + _bodyBall[ AVATAR_JOINT_HEAD_BASE ].radius;
|
||||
_height = _skeleton.getHeight() + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius + _bodyBall[ BODY_BALL_HEAD_BASE ].radius;
|
||||
_maxArmLength = _skeleton.getArmLength();
|
||||
_pelvisStandingHeight = _skeleton.getPelvisStandingHeight() + _bodyBall[ AVATAR_JOINT_LEFT_HEEL ].radius;
|
||||
_pelvisFloatingHeight = _skeleton.getPelvisFloatingHeight() + _bodyBall[ AVATAR_JOINT_LEFT_HEEL ].radius;
|
||||
|
@ -118,44 +117,120 @@ _isMouseTurningRight(false)
|
|||
|
||||
void Avatar::initializeBodyBalls() {
|
||||
|
||||
for (int b=0; b<NUM_AVATAR_JOINTS; b++) {
|
||||
_bodyBall[b].isCollidable = true;
|
||||
_ballSpringsInitialized = false; //this gets set to true on the first update pass...
|
||||
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
_bodyBall[b].parentJoint = AVATAR_JOINT_NULL;
|
||||
_bodyBall[b].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[b].position = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[b].velocity = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[b].radius = 0.0;
|
||||
_bodyBall[b].touchForce = 0.0;
|
||||
_bodyBall[b].isCollidable = true;
|
||||
_bodyBall[b].jointTightness = BODY_SPRING_DEFAULT_TIGHTNESS;
|
||||
}
|
||||
|
||||
// specify the radius of each ball
|
||||
_bodyBall[ BODY_BALL_PELVIS ].radius = 0.07;
|
||||
_bodyBall[ BODY_BALL_TORSO ].radius = 0.065;
|
||||
_bodyBall[ BODY_BALL_CHEST ].radius = 0.08;
|
||||
_bodyBall[ BODY_BALL_NECK_BASE ].radius = 0.03;
|
||||
_bodyBall[ BODY_BALL_HEAD_BASE ].radius = 0.07;
|
||||
_bodyBall[ BODY_BALL_LEFT_COLLAR ].radius = 0.04;
|
||||
_bodyBall[ BODY_BALL_LEFT_SHOULDER ].radius = 0.03;
|
||||
_bodyBall[ BODY_BALL_LEFT_ELBOW ].radius = 0.02;
|
||||
_bodyBall[ BODY_BALL_LEFT_WRIST ].radius = 0.02;
|
||||
_bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].radius = 0.01;
|
||||
_bodyBall[ BODY_BALL_RIGHT_COLLAR ].radius = 0.04;
|
||||
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].radius = 0.03;
|
||||
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].radius = 0.02;
|
||||
_bodyBall[ BODY_BALL_RIGHT_WRIST ].radius = 0.02;
|
||||
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].radius = 0.01;
|
||||
_bodyBall[ BODY_BALL_LEFT_HIP ].radius = 0.04;
|
||||
|
||||
//_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].radius = 0.03;
|
||||
|
||||
_bodyBall[ BODY_BALL_LEFT_KNEE ].radius = 0.025;
|
||||
_bodyBall[ BODY_BALL_LEFT_HEEL ].radius = 0.025;
|
||||
_bodyBall[ BODY_BALL_LEFT_TOES ].radius = 0.025;
|
||||
_bodyBall[ BODY_BALL_RIGHT_HIP ].radius = 0.04;
|
||||
_bodyBall[ BODY_BALL_RIGHT_KNEE ].radius = 0.025;
|
||||
_bodyBall[ BODY_BALL_RIGHT_HEEL ].radius = 0.025;
|
||||
_bodyBall[ BODY_BALL_RIGHT_TOES ].radius = 0.025;
|
||||
|
||||
// specify the radii of the joints
|
||||
_bodyBall[ AVATAR_JOINT_PELVIS ].radius = 0.07;
|
||||
_bodyBall[ AVATAR_JOINT_TORSO ].radius = 0.065;
|
||||
_bodyBall[ AVATAR_JOINT_CHEST ].radius = 0.08;
|
||||
_bodyBall[ AVATAR_JOINT_NECK_BASE ].radius = 0.03;
|
||||
_bodyBall[ AVATAR_JOINT_HEAD_BASE ].radius = 0.07;
|
||||
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_COLLAR ].radius = 0.04;
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_SHOULDER ].radius = 0.03;
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_ELBOW ].radius = 0.02;
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_WRIST ].radius = 0.02;
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_FINGERTIPS ].radius = 0.01;
|
||||
// specify the parent joint for each ball
|
||||
_bodyBall[ BODY_BALL_PELVIS ].parentJoint = AVATAR_JOINT_PELVIS;
|
||||
_bodyBall[ BODY_BALL_TORSO ].parentJoint = AVATAR_JOINT_TORSO;
|
||||
_bodyBall[ BODY_BALL_CHEST ].parentJoint = AVATAR_JOINT_CHEST;
|
||||
_bodyBall[ BODY_BALL_NECK_BASE ].parentJoint = AVATAR_JOINT_NECK_BASE;
|
||||
_bodyBall[ BODY_BALL_HEAD_BASE ].parentJoint = AVATAR_JOINT_HEAD_BASE;
|
||||
_bodyBall[ BODY_BALL_HEAD_TOP ].parentJoint = AVATAR_JOINT_HEAD_TOP;
|
||||
_bodyBall[ BODY_BALL_LEFT_COLLAR ].parentJoint = AVATAR_JOINT_LEFT_COLLAR;
|
||||
_bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentJoint = AVATAR_JOINT_LEFT_SHOULDER;
|
||||
_bodyBall[ BODY_BALL_LEFT_ELBOW ].parentJoint = AVATAR_JOINT_LEFT_ELBOW;
|
||||
_bodyBall[ BODY_BALL_LEFT_WRIST ].parentJoint = AVATAR_JOINT_LEFT_WRIST;
|
||||
_bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentJoint = AVATAR_JOINT_LEFT_FINGERTIPS;
|
||||
_bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentJoint = AVATAR_JOINT_RIGHT_COLLAR;
|
||||
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentJoint = AVATAR_JOINT_RIGHT_SHOULDER;
|
||||
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentJoint = AVATAR_JOINT_RIGHT_ELBOW;
|
||||
_bodyBall[ BODY_BALL_RIGHT_WRIST ].parentJoint = AVATAR_JOINT_RIGHT_WRIST;
|
||||
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentJoint = AVATAR_JOINT_RIGHT_FINGERTIPS;
|
||||
_bodyBall[ BODY_BALL_LEFT_HIP ].parentJoint = AVATAR_JOINT_LEFT_HIP;
|
||||
_bodyBall[ BODY_BALL_LEFT_KNEE ].parentJoint = AVATAR_JOINT_LEFT_KNEE;
|
||||
_bodyBall[ BODY_BALL_LEFT_HEEL ].parentJoint = AVATAR_JOINT_LEFT_HEEL;
|
||||
_bodyBall[ BODY_BALL_LEFT_TOES ].parentJoint = AVATAR_JOINT_LEFT_TOES;
|
||||
_bodyBall[ BODY_BALL_RIGHT_HIP ].parentJoint = AVATAR_JOINT_RIGHT_HIP;
|
||||
_bodyBall[ BODY_BALL_RIGHT_KNEE ].parentJoint = AVATAR_JOINT_RIGHT_KNEE;
|
||||
_bodyBall[ BODY_BALL_RIGHT_HEEL ].parentJoint = AVATAR_JOINT_RIGHT_HEEL;
|
||||
_bodyBall[ BODY_BALL_RIGHT_TOES ].parentJoint = AVATAR_JOINT_RIGHT_TOES;
|
||||
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_COLLAR ].radius = 0.04;
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_SHOULDER ].radius = 0.03;
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_ELBOW ].radius = 0.02;
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_WRIST ].radius = 0.02;
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_FINGERTIPS ].radius = 0.01;
|
||||
//_bodyBall[ BODY_BALL_LEFT_MID_THIGH].parentJoint = AVATAR_JOINT_LEFT_HIP;
|
||||
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_HIP ].radius = 0.04;
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_KNEE ].radius = 0.025;
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_HEEL ].radius = 0.025;
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_TOES ].radius = 0.025;
|
||||
// specify the parent offset for each ball
|
||||
_bodyBall[ BODY_BALL_PELVIS ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_TORSO ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_CHEST ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_NECK_BASE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_HEAD_BASE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_HEAD_TOP ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_LEFT_COLLAR ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_LEFT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_LEFT_WRIST ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_RIGHT_WRIST ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_LEFT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_LEFT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_LEFT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_LEFT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_RIGHT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_RIGHT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_RIGHT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[ BODY_BALL_RIGHT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
|
||||
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_HIP ].radius = 0.04;
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_KNEE ].radius = 0.025;
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_HEEL ].radius = 0.025;
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_TOES ].radius = 0.025;
|
||||
|
||||
//_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].parentBall = BODY_BALL_LEFT_HIP;
|
||||
|
||||
// _bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_MID_THIGH;
|
||||
_bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_HIP;
|
||||
|
||||
_bodyBall[ BODY_BALL_LEFT_HEEL ].parentBall = BODY_BALL_LEFT_KNEE;
|
||||
_bodyBall[ BODY_BALL_LEFT_TOES ].parentBall = BODY_BALL_LEFT_HEEL;
|
||||
_bodyBall[ BODY_BALL_RIGHT_HIP ].parentBall = BODY_BALL_PELVIS;
|
||||
_bodyBall[ BODY_BALL_RIGHT_KNEE ].parentBall = BODY_BALL_RIGHT_HIP;
|
||||
_bodyBall[ BODY_BALL_RIGHT_HEEL ].parentBall = BODY_BALL_RIGHT_KNEE;
|
||||
_bodyBall[ BODY_BALL_RIGHT_TOES ].parentBall = BODY_BALL_RIGHT_HEEL;
|
||||
|
||||
/*
|
||||
// to aid in hand-shaking and hand-holding, the right hand is not collidable
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_ELBOW ].isCollidable = false;
|
||||
|
@ -170,6 +245,11 @@ Avatar::~Avatar() {
|
|||
delete _balls;
|
||||
}
|
||||
|
||||
void Avatar::init() {
|
||||
_voxels.init();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
void Avatar::reset() {
|
||||
_head.reset();
|
||||
}
|
||||
|
@ -275,6 +355,25 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
// update avatar skeleton
|
||||
_skeleton.update(deltaTime, getOrientation(), _position);
|
||||
|
||||
//determine the lengths of the body springs now that we have updated the skeleton at least once
|
||||
if (!_ballSpringsInitialized) {
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
|
||||
glm::vec3 targetPosition
|
||||
= _skeleton.joint[_bodyBall[b].parentJoint].position
|
||||
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
|
||||
|
||||
glm::vec3 parentTargetPosition
|
||||
= _skeleton.joint[_bodyBall[b].parentJoint].position
|
||||
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
|
||||
|
||||
_bodyBall[b].springLength = glm::length(targetPosition - parentTargetPosition);
|
||||
}
|
||||
|
||||
_ballSpringsInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
// if this is not my avatar, then hand position comes from transmitted data
|
||||
if (_owningAgent) {
|
||||
_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = _handPosition;
|
||||
|
@ -310,9 +409,9 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
}
|
||||
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 front = orientation * AVATAR_FRONT;
|
||||
glm::vec3 right = orientation * AVATAR_RIGHT;
|
||||
glm::vec3 up = orientation * AVATAR_UP;
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
|
||||
// driving the avatar around should only apply if this is my avatar (as opposed to an avatar being driven remotely)
|
||||
const float THRUST_MAG = 600.0f;
|
||||
|
@ -461,17 +560,17 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
_bodyBall[ AVATAR_JOINT_NECK_BASE ].position += headLean * 0.7f;
|
||||
_bodyBall[ AVATAR_JOINT_HEAD_BASE ].position += headLean * 1.0f;
|
||||
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_COLLAR ].position += headLean * 0.6f;
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_SHOULDER ].position += headLean * 0.6f;
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_ELBOW ].position += headLean * 0.2f;
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_WRIST ].position += headLean * 0.1f;
|
||||
_bodyBall[ AVATAR_JOINT_LEFT_FINGERTIPS ].position += headLean * 0.0f;
|
||||
_bodyBall[ BODY_BALL_LEFT_COLLAR ].position += headLean * 0.6f;
|
||||
_bodyBall[ BODY_BALL_LEFT_SHOULDER ].position += headLean * 0.6f;
|
||||
_bodyBall[ BODY_BALL_LEFT_ELBOW ].position += headLean * 0.2f;
|
||||
_bodyBall[ BODY_BALL_LEFT_WRIST ].position += headLean * 0.1f;
|
||||
_bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].position += headLean * 0.0f;
|
||||
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_COLLAR ].position += headLean * 0.6f;
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_SHOULDER ].position += headLean * 0.6f;
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_ELBOW ].position += headLean * 0.2f;
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_WRIST ].position += headLean * 0.1f;
|
||||
_bodyBall[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position += headLean * 0.0f;
|
||||
_bodyBall[ BODY_BALL_RIGHT_COLLAR ].position += headLean * 0.6f;
|
||||
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].position += headLean * 0.6f;
|
||||
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].position += headLean * 0.2f;
|
||||
_bodyBall[ BODY_BALL_RIGHT_WRIST ].position += headLean * 0.1f;
|
||||
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].position += headLean * 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -485,8 +584,8 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
}
|
||||
|
||||
_head.setBodyRotation (glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||
_head.setPosition(_bodyBall[ AVATAR_JOINT_HEAD_BASE ].position);
|
||||
_head.setScale (_bodyBall[ AVATAR_JOINT_HEAD_BASE ].radius);
|
||||
_head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position);
|
||||
_head.setScale (_bodyBall[ BODY_BALL_HEAD_BASE ].radius);
|
||||
_head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]));
|
||||
_head.simulate(deltaTime, !_owningAgent);
|
||||
|
||||
|
@ -650,32 +749,29 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
|
|||
}
|
||||
|
||||
_avatarTouch.setMyHandState(_handState);
|
||||
_avatarTouch.setMyHandPosition(_bodyBall[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position);
|
||||
_avatarTouch.setMyHandPosition(_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].position);
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::updateCollisionWithSphere(glm::vec3 position, float radius, float deltaTime) {
|
||||
float myBodyApproximateBoundingRadius = 1.0f;
|
||||
glm::vec3 vectorFromMyBodyToBigSphere(_position - position);
|
||||
bool jointCollision = false;
|
||||
|
||||
float distanceToBigSphere = glm::length(vectorFromMyBodyToBigSphere);
|
||||
if (distanceToBigSphere < myBodyApproximateBoundingRadius + radius) {
|
||||
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) {
|
||||
glm::vec3 vectorFromJointToBigSphereCenter(_bodyBall[b].position - position);
|
||||
float distanceToBigSphereCenter = glm::length(vectorFromJointToBigSphereCenter);
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
glm::vec3 vectorFromBallToBigSphereCenter(_bodyBall[b].position - position);
|
||||
float distanceToBigSphereCenter = glm::length(vectorFromBallToBigSphereCenter);
|
||||
float combinedRadius = _bodyBall[b].radius + radius;
|
||||
|
||||
if (distanceToBigSphereCenter < combinedRadius) {
|
||||
jointCollision = true;
|
||||
if (distanceToBigSphereCenter > 0.0) {
|
||||
glm::vec3 directionVector = vectorFromJointToBigSphereCenter / distanceToBigSphereCenter;
|
||||
glm::vec3 directionVector = vectorFromBallToBigSphereCenter / distanceToBigSphereCenter;
|
||||
|
||||
float penetration = 1.0 - (distanceToBigSphereCenter / combinedRadius);
|
||||
glm::vec3 collisionForce = vectorFromJointToBigSphereCenter * penetration;
|
||||
|
||||
_bodyBall[b].velocity += collisionForce * 0.0f * deltaTime;
|
||||
_velocity += collisionForce * 40.0f * deltaTime;
|
||||
glm::vec3 collisionForce = vectorFromBallToBigSphereCenter * penetration;
|
||||
|
||||
_velocity += collisionForce * 40.0f * deltaTime;
|
||||
_bodyBall[b].position = position + directionVector * combinedRadius;
|
||||
}
|
||||
}
|
||||
|
@ -755,7 +851,7 @@ void Avatar::updateAvatarCollisions(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
//detect collisions with other avatars and respond
|
||||
// detect collisions with other avatars and respond
|
||||
void Avatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTime) {
|
||||
|
||||
glm::vec3 bodyPushForce = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
|
@ -770,7 +866,7 @@ void Avatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTime
|
|||
glm::vec3 vectorBetweenJoints(_bodyBall[b].position - otherAvatar->_bodyBall[o].position);
|
||||
float distanceBetweenJoints = glm::length(vectorBetweenJoints);
|
||||
|
||||
if (distanceBetweenJoints > 0.0) { // to avoid divide by zero
|
||||
if (distanceBetweenBalls > 0.0) { // to avoid divide by zero
|
||||
float combinedRadius = _bodyBall[b].radius + otherAvatar->_bodyBall[o].radius;
|
||||
|
||||
// check for collision
|
||||
|
@ -778,7 +874,7 @@ void Avatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTime
|
|||
glm::vec3 directionVector = vectorBetweenJoints / distanceBetweenJoints;
|
||||
|
||||
// push balls away from each other and apply friction
|
||||
float penetration = 1.0f - (distanceBetweenJoints / (combinedRadius * COLLISION_RADIUS_SCALAR));
|
||||
float penetration = 1.0f - (distanceBetweenBalls / (combinedRadius * COLLISION_RADIUS_SCALAR));
|
||||
|
||||
glm::vec3 ballPushForce = directionVector * COLLISION_BALL_FORCE * penetration * deltaTime;
|
||||
bodyPushForce += directionVector * COLLISION_BODY_FORCE * penetration * deltaTime;
|
||||
|
@ -857,7 +953,7 @@ void Avatar::render(bool lookingInMirror) {
|
|||
}
|
||||
glPushMatrix();
|
||||
|
||||
glm::vec3 chatPosition = _bodyBall[AVATAR_JOINT_HEAD_BASE].position + getBodyUpDirection() * chatMessageHeight;
|
||||
glm::vec3 chatPosition = _bodyBall[BODY_BALL_HEAD_BASE].position + getBodyUpDirection() * chatMessageHeight;
|
||||
glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z);
|
||||
glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation();
|
||||
glm::vec3 chatAxis = glm::axis(chatRotation);
|
||||
|
@ -893,16 +989,21 @@ void Avatar::render(bool lookingInMirror) {
|
|||
}
|
||||
|
||||
void Avatar::resetBodyBalls() {
|
||||
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) {
|
||||
_bodyBall[b].position = _skeleton.joint[b].position;
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
|
||||
glm::vec3 targetPosition
|
||||
= _skeleton.joint[_bodyBall[b].parentJoint].position
|
||||
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
|
||||
|
||||
_bodyBall[b].position = targetPosition; // put ball on target position
|
||||
_bodyBall[b].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::updateBodyBalls(float deltaTime) {
|
||||
// Check for a large repositioning, and re-initialize balls if this has happened
|
||||
// Check for a large repositioning, and re-initialize balls if this has happened
|
||||
const float BEYOND_BODY_SPRING_RANGE = 2.f;
|
||||
if (glm::length(_position - _bodyBall[AVATAR_JOINT_PELVIS].position) > BEYOND_BODY_SPRING_RANGE) {
|
||||
if (glm::length(_position - _bodyBall[BODY_BALL_PELVIS].position) > BEYOND_BODY_SPRING_RANGE) {
|
||||
resetBodyBalls();
|
||||
}
|
||||
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) {
|
||||
|
@ -926,17 +1027,33 @@ void Avatar::updateBodyBalls(float deltaTime) {
|
|||
if (_skeleton.joint[b].parent != AVATAR_JOINT_NULL) {
|
||||
_bodyBall[_skeleton.joint[b].parent].velocity += springDirection * force;
|
||||
}
|
||||
|
||||
length = glm::length(springVector);
|
||||
|
||||
if (length > 0.0f) { // to avoid divide by zero
|
||||
glm::vec3 springDirection = springVector / length;
|
||||
|
||||
float force = (length - _skeleton.joint[b].length) * BODY_SPRING_FORCE * deltaTime;
|
||||
_bodyBall[b].velocity -= springDirection * force;
|
||||
|
||||
if (_bodyBall[b].parentBall != BODY_BALL_NULL) {
|
||||
_bodyBall[_bodyBall[b].parentBall].velocity += springDirection * force;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply tightness force - (causing ball position to be close to skeleton joint position)
|
||||
_bodyBall[b].velocity += (_skeleton.joint[b].position - _bodyBall[b].position) * _bodyBall[b].jointTightness * deltaTime;
|
||||
glm::vec3 targetPosition
|
||||
= _skeleton.joint[_bodyBall[b].parentJoint].position
|
||||
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
|
||||
|
||||
_bodyBall[b].velocity += (targetPosition - _bodyBall[b].position) * _bodyBall[b].jointTightness * deltaTime;
|
||||
|
||||
// apply decay
|
||||
float decay = 1.0 - BODY_SPRING_DECAY * deltaTime;
|
||||
if (decay > 0.0) {
|
||||
_bodyBall[b].velocity *= decay;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
_bodyBall[b].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
|
@ -947,8 +1064,16 @@ void Avatar::updateBodyBalls(float deltaTime) {
|
|||
}
|
||||
*/
|
||||
|
||||
//update position by velocity...
|
||||
// update position by velocity...
|
||||
_bodyBall[b].position += _bodyBall[b].velocity * deltaTime;
|
||||
|
||||
// update rotation
|
||||
const float SMALL_SPRING_LENGTH = 0.001f; // too-small springs can change direction rapidly
|
||||
if (_skeleton.joint[b].parent == AVATAR_JOINT_NULL || length < SMALL_SPRING_LENGTH) {
|
||||
_bodyBall[b].rotation = orientation * _skeleton.joint[_bodyBall[b].parentJoint].absoluteBindPoseRotation;
|
||||
} else {
|
||||
_bodyBall[b].rotation = rotationBetween(jointDirection, springVector) * orientation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -990,14 +1115,14 @@ void Avatar::updateArmIKAndConstraints(float deltaTime) {
|
|||
|
||||
glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 currentUp = orientation * AVATAR_UP;
|
||||
glm::vec3 currentUp = orientation * IDENTITY_UP;
|
||||
float angle = glm::degrees(acosf(glm::clamp(glm::dot(currentUp, _worldUpDirection), -1.0f, 1.0f)));
|
||||
if (angle < EPSILON) {
|
||||
return glm::quat();
|
||||
}
|
||||
glm::vec3 axis;
|
||||
if (angle > 179.99f) { // 180 degree rotation; must use another axis
|
||||
axis = orientation * AVATAR_RIGHT;
|
||||
axis = orientation * IDENTITY_RIGHT;
|
||||
} else {
|
||||
axis = glm::normalize(glm::cross(currentUp, _worldUpDirection));
|
||||
}
|
||||
|
@ -1021,7 +1146,7 @@ void Avatar::renderBody(bool lookingInMirror) {
|
|||
}
|
||||
|
||||
// Always render other people, and render myself when beyond threshold distance
|
||||
if (b == AVATAR_JOINT_HEAD_BASE) { // the head is rendered as a special
|
||||
if (b == BODY_BALL_HEAD_BASE) { // the head is rendered as a special
|
||||
if (lookingInMirror || _owningAgent || distanceToCamera > RENDER_OPAQUE_BEYOND * 0.5) {
|
||||
_head.render(lookingInMirror, _cameraPosition, alpha);
|
||||
}
|
||||
|
@ -1064,15 +1189,15 @@ void Avatar::renderBody(bool lookingInMirror) {
|
|||
&& (b != AVATAR_JOINT_RIGHT_SHOULDER)) {
|
||||
glColor3fv(DARK_SKIN_COLOR);
|
||||
|
||||
float r1 = _bodyBall[_skeleton.joint[b].parent ].radius * 0.8;
|
||||
float r2 = _bodyBall[b ].radius * 0.8;
|
||||
if (b == AVATAR_JOINT_HEAD_BASE) {
|
||||
float r1 = _bodyBall[_bodyBall[b].parentBall ].radius * 0.8;
|
||||
float r2 = _bodyBall[b].radius * 0.8;
|
||||
if (b == BODY_BALL_HEAD_BASE) {
|
||||
r1 *= 0.5f;
|
||||
}
|
||||
renderJointConnectingCone
|
||||
(
|
||||
_bodyBall[_skeleton.joint[b].parent ].position,
|
||||
_bodyBall[b ].position, r2, r2
|
||||
_bodyBall[_bodyBall[b].parentBall].position,
|
||||
_bodyBall[b].position, r2, r2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1110,26 +1235,41 @@ void Avatar::setHeadFromGyros(glm::vec3* eulerAngles, glm::vec3* angularVelocity
|
|||
_head.setYaw (angles.x);
|
||||
_head.setPitch(angles.y);
|
||||
_head.setRoll (angles.z);
|
||||
//printLog("Y/P/R: %3.1f, %3.1f, %3.1f\n", angles.x, angles.y, angles.z);
|
||||
// printLog("Y/P/R: %3.1f, %3.1f, %3.1f\n", angles.x, angles.y, angles.z);
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::writeAvatarDataToFile() {
|
||||
Application::getInstance()->setSetting("avatarPos", _position);
|
||||
Application::getInstance()->setSetting("avatarRotation", glm::vec3(_bodyYaw, _bodyPitch, _bodyRoll));
|
||||
void Avatar::loadData(QSettings* set) {
|
||||
set->beginGroup("Avatar");
|
||||
|
||||
_bodyYaw = set->value("bodyYaw", _bodyYaw).toFloat();
|
||||
_bodyPitch = set->value("bodyPitch", _bodyPitch).toFloat();
|
||||
_bodyRoll = set->value("bodyRoll", _bodyRoll).toFloat();
|
||||
|
||||
_position.x = set->value("position_x", _position.x).toFloat();
|
||||
_position.y = set->value("position_y", _position.y).toFloat();
|
||||
_position.z = set->value("position_z", _position.z).toFloat();
|
||||
|
||||
set->endGroup();
|
||||
}
|
||||
|
||||
void Avatar::readAvatarDataFromFile() {
|
||||
glm::vec3 readPosition;
|
||||
glm::vec3 readRotation;
|
||||
|
||||
Application::getInstance()->getSetting("avatarPos", readPosition, glm::vec3(6.1f, 0, 1.4f));
|
||||
Application::getInstance()->getSetting("avatarRotation", readRotation, glm::vec3(0, 0, 0));
|
||||
|
||||
_bodyYaw = readRotation.x;
|
||||
_bodyPitch = readRotation.y;
|
||||
_bodyRoll = readRotation.z;
|
||||
_position = readPosition;
|
||||
void Avatar::getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const {
|
||||
position = _bodyBall[jointID].position;
|
||||
rotation = _bodyBall[jointID].rotation;
|
||||
}
|
||||
|
||||
void Avatar::saveData(QSettings* set) {
|
||||
set->beginGroup("Avatar");
|
||||
|
||||
set->setValue("bodyYaw", _bodyYaw);
|
||||
set->setValue("bodyPitch", _bodyPitch);
|
||||
set->setValue("bodyRoll", _bodyRoll);
|
||||
|
||||
set->setValue("position_x", _position.x);
|
||||
set->setValue("position_y", _position.y);
|
||||
set->setValue("position_z", _position.z);
|
||||
|
||||
set->endGroup();
|
||||
}
|
||||
|
||||
// render a makeshift cone section that serves as a body part connecting joint spheres
|
||||
|
@ -1178,4 +1318,4 @@ void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2,
|
|||
}
|
||||
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,10 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <AvatarData.h>
|
||||
#include <QSettings>
|
||||
#include "world.h"
|
||||
#include "AvatarTouch.h"
|
||||
#include "AvatarVoxelSystem.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "SerialInterface.h"
|
||||
#include "Balls.h"
|
||||
|
@ -20,6 +22,40 @@
|
|||
#include "Skeleton.h"
|
||||
#include "Transmitter.h"
|
||||
|
||||
|
||||
enum AvatarBodyBallID
|
||||
{
|
||||
BODY_BALL_NULL = -1,
|
||||
BODY_BALL_PELVIS,
|
||||
BODY_BALL_TORSO,
|
||||
BODY_BALL_CHEST,
|
||||
BODY_BALL_NECK_BASE,
|
||||
BODY_BALL_HEAD_BASE,
|
||||
BODY_BALL_HEAD_TOP,
|
||||
BODY_BALL_LEFT_COLLAR,
|
||||
BODY_BALL_LEFT_SHOULDER,
|
||||
BODY_BALL_LEFT_ELBOW,
|
||||
BODY_BALL_LEFT_WRIST,
|
||||
BODY_BALL_LEFT_FINGERTIPS,
|
||||
BODY_BALL_RIGHT_COLLAR,
|
||||
BODY_BALL_RIGHT_SHOULDER,
|
||||
BODY_BALL_RIGHT_ELBOW,
|
||||
BODY_BALL_RIGHT_WRIST,
|
||||
BODY_BALL_RIGHT_FINGERTIPS,
|
||||
BODY_BALL_LEFT_HIP,
|
||||
BODY_BALL_LEFT_KNEE,
|
||||
BODY_BALL_LEFT_HEEL,
|
||||
BODY_BALL_LEFT_TOES,
|
||||
BODY_BALL_RIGHT_HIP,
|
||||
BODY_BALL_RIGHT_KNEE,
|
||||
BODY_BALL_RIGHT_HEEL,
|
||||
BODY_BALL_RIGHT_TOES,
|
||||
|
||||
//TEST!
|
||||
//BODY_BALL_LEFT_MID_THIGH,
|
||||
NUM_AVATAR_BODY_BALLS
|
||||
};
|
||||
|
||||
enum DriveKeys
|
||||
{
|
||||
FWD = 0,
|
||||
|
@ -46,6 +82,7 @@ public:
|
|||
Avatar(Agent* owningAgent = NULL);
|
||||
~Avatar();
|
||||
|
||||
void init();
|
||||
void reset();
|
||||
void simulate(float deltaTime, Transmitter* transmitter);
|
||||
void updateHeadFromGyros(float frametime, SerialInterface * serialInterface);
|
||||
|
@ -64,19 +101,16 @@ public:
|
|||
void setOrientation (const glm::quat& orientation);
|
||||
|
||||
//getters
|
||||
|
||||
float getHeadYawRate () const { return _head.yawRate;}
|
||||
float getBodyYaw () const { return _bodyYaw;}
|
||||
bool getIsNearInteractingOther() const { return _avatarTouch.getAbleToReachOtherAvatar();}
|
||||
const glm::vec3& getHeadPosition () const { return _skeleton.joint[ AVATAR_JOINT_HEAD_BASE ].position;}
|
||||
const glm::vec3& getSpringyHeadPosition () const { return _bodyBall[ AVATAR_JOINT_HEAD_BASE ].position;}
|
||||
const glm::vec3& getJointPosition (AvatarJointID j) const { return _bodyBall[j].position;}
|
||||
|
||||
glm::vec3 getBodyRightDirection () const { return getOrientation() * AVATAR_RIGHT; }
|
||||
glm::vec3 getBodyUpDirection () const { return getOrientation() * AVATAR_UP; }
|
||||
glm::vec3 getBodyFrontDirection () const { return getOrientation() * AVATAR_FRONT; }
|
||||
|
||||
|
||||
bool isInitialized () const { return _initialized;}
|
||||
const Skeleton& getSkeleton () const { return _skeleton;}
|
||||
float getHeadYawRate () const { return _head.yawRate;}
|
||||
float getBodyYaw () const { return _bodyYaw;}
|
||||
bool getIsNearInteractingOther () const { return _avatarTouch.getAbleToReachOtherAvatar();}
|
||||
const glm::vec3& getHeadJointPosition () const { return _skeleton.joint[ AVATAR_JOINT_HEAD_BASE ].position;}
|
||||
const glm::vec3& getBallPosition (AvatarJointID j) const { return _bodyBall[j].position;}
|
||||
glm::vec3 getBodyRightDirection () const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getBodyUpDirection () const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getBodyFrontDirection () const { return getOrientation() * IDENTITY_FRONT; }
|
||||
const glm::vec3& getVelocity () const { return _velocity;}
|
||||
float getSpeed () const { return _speed;}
|
||||
float getHeight () const { return _height;}
|
||||
|
@ -87,6 +121,8 @@ public:
|
|||
glm::quat getOrientation () const;
|
||||
glm::quat getWorldAlignedOrientation() const;
|
||||
|
||||
AvatarVoxelSystem* getVoxels() { return &_voxels; }
|
||||
|
||||
// Set what driving keys are being pressed to control thrust levels
|
||||
void setDriveKeys(int key, bool val) { _driveKeys[key] = val; };
|
||||
bool getDriveKeys(int key) { return _driveKeys[key]; };
|
||||
|
@ -95,6 +131,13 @@ public:
|
|||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
glm::vec3 getThrust() { return _thrust; };
|
||||
|
||||
// get/set avatar data
|
||||
void saveData(QSettings* set);
|
||||
void loadData(QSettings* set);
|
||||
|
||||
// Get the position/rotation of a single body ball
|
||||
void getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const;
|
||||
|
||||
//read/write avatar data
|
||||
void writeAvatarDataToFile();
|
||||
void readAvatarDataFromFile();
|
||||
|
@ -106,16 +149,23 @@ private:
|
|||
|
||||
struct AvatarBall
|
||||
{
|
||||
glm::vec3 position;
|
||||
glm::vec3 velocity;
|
||||
float jointTightness;
|
||||
float radius;
|
||||
bool isCollidable;
|
||||
float touchForce;
|
||||
AvatarJointID parentJoint; // the skeletal joint that serves as a reference for determining the position
|
||||
glm::vec3 parentOffset; // a 3D vector in the frame of reference of the parent skeletal joint
|
||||
AvatarBodyBallID parentBall; // the ball to which this ball is constrained for spring forces
|
||||
glm::vec3 position; // the actual dynamic position of the ball at any given time
|
||||
glm::quat rotation; // the rotation of the ball
|
||||
glm::vec3 velocity; // the velocity of the ball
|
||||
float springLength; // the ideal length of the spring between this ball and its parentBall
|
||||
float jointTightness; // how tightly the ball position attempts to stay at its ideal position (determined by parentOffset)
|
||||
float radius; // the radius of the ball
|
||||
bool isCollidable; // whether or not the ball responds to collisions
|
||||
float touchForce; // a scalar determining the amount that the cursor (or hand) is penetrating the ball
|
||||
};
|
||||
|
||||
bool _initialized;
|
||||
Head _head;
|
||||
Skeleton _skeleton;
|
||||
bool _ballSpringsInitialized;
|
||||
float _TEST_bigSphereRadius;
|
||||
glm::vec3 _TEST_bigSpherePosition;
|
||||
bool _mousePressed;
|
||||
|
@ -123,8 +173,7 @@ private:
|
|||
float _bodyYawDelta;
|
||||
float _bodyRollDelta;
|
||||
glm::vec3 _movedHandOffset;
|
||||
glm::quat _rotation; // the rotation of the avatar body as a whole expressed as a quaternion
|
||||
AvatarBall _bodyBall[ NUM_AVATAR_JOINTS ];
|
||||
AvatarBall _bodyBall[ NUM_AVATAR_BODY_BALLS ];
|
||||
AvatarMode _mode;
|
||||
glm::vec3 _cameraPosition;
|
||||
glm::vec3 _handHoldingPosition;
|
||||
|
@ -148,6 +197,8 @@ private:
|
|||
float _cumulativeMouseYaw;
|
||||
bool _isMouseTurningRight;
|
||||
|
||||
AvatarVoxelSystem _voxels;
|
||||
|
||||
// private methods...
|
||||
glm::vec3 caclulateAverageEyePosition() { return _head.caclulateAverageEyePosition(); } // get the position smack-dab between the eyes (for lookat)
|
||||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
enum AvatarHandState
|
||||
{
|
||||
HAND_STATE_NULL = -1,
|
||||
HAND_STATE_NULL = 0,
|
||||
HAND_STATE_OPEN,
|
||||
HAND_STATE_GRASPING,
|
||||
HAND_STATE_POINTING,
|
||||
|
|
279
interface/src/AvatarVoxelSystem.cpp
Normal file
279
interface/src/AvatarVoxelSystem.cpp
Normal file
|
@ -0,0 +1,279 @@
|
|||
//
|
||||
// AvatarVoxelSystem.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/31/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
|
||||
#include <GeometryUtil.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Avatar.h"
|
||||
#include "AvatarVoxelSystem.h"
|
||||
#include "renderer/ProgramObject.h"
|
||||
|
||||
const float AVATAR_TREE_SCALE = 1.0f;
|
||||
const int MAX_VOXELS_PER_AVATAR = 2000;
|
||||
const int BONE_ELEMENTS_PER_VOXEL = BONE_ELEMENTS_PER_VERTEX * VERTICES_PER_VOXEL;
|
||||
|
||||
AvatarVoxelSystem::AvatarVoxelSystem(Avatar* avatar) :
|
||||
VoxelSystem(AVATAR_TREE_SCALE, MAX_VOXELS_PER_AVATAR),
|
||||
_avatar(avatar), _voxelReply(0) {
|
||||
}
|
||||
|
||||
AvatarVoxelSystem::~AvatarVoxelSystem() {
|
||||
delete[] _readBoneIndicesArray;
|
||||
delete[] _readBoneWeightsArray;
|
||||
delete[] _writeBoneIndicesArray;
|
||||
delete[] _writeBoneWeightsArray;
|
||||
}
|
||||
|
||||
ProgramObject* AvatarVoxelSystem::_skinProgram = 0;
|
||||
int AvatarVoxelSystem::_boneMatricesLocation;
|
||||
int AvatarVoxelSystem::_boneIndicesLocation;
|
||||
int AvatarVoxelSystem::_boneWeightsLocation;
|
||||
|
||||
void AvatarVoxelSystem::init() {
|
||||
VoxelSystem::init();
|
||||
|
||||
// prep the data structures for incoming voxel data
|
||||
_writeBoneIndicesArray = new GLubyte[BONE_ELEMENTS_PER_VOXEL * _maxVoxels];
|
||||
_readBoneIndicesArray = new GLubyte[BONE_ELEMENTS_PER_VOXEL * _maxVoxels];
|
||||
|
||||
_writeBoneWeightsArray = new GLfloat[BONE_ELEMENTS_PER_VOXEL * _maxVoxels];
|
||||
_readBoneWeightsArray = new GLfloat[BONE_ELEMENTS_PER_VOXEL * _maxVoxels];
|
||||
|
||||
// VBO for the boneIndicesArray
|
||||
glGenBuffers(1, &_vboBoneIndicesID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneIndicesID);
|
||||
glBufferData(GL_ARRAY_BUFFER, BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);
|
||||
|
||||
// VBO for the boneWeightsArray
|
||||
glGenBuffers(1, &_vboBoneWeightsID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneWeightsID);
|
||||
glBufferData(GL_ARRAY_BUFFER, BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);
|
||||
|
||||
// load our skin program if this is the first avatar system to initialize
|
||||
if (_skinProgram != 0) {
|
||||
return;
|
||||
}
|
||||
_skinProgram = new ProgramObject();
|
||||
_skinProgram->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/skin_voxels.vert");
|
||||
_skinProgram->link();
|
||||
|
||||
_boneMatricesLocation = _skinProgram->uniformLocation("boneMatrices");
|
||||
_boneIndicesLocation = _skinProgram->attributeLocation("boneIndices");
|
||||
_boneWeightsLocation = _skinProgram->attributeLocation("boneWeights");
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::removeOutOfView() {
|
||||
// no-op for now
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::loadVoxelsFromURL(const QUrl& url) {
|
||||
// cancel any current download
|
||||
if (_voxelReply != 0) {
|
||||
delete _voxelReply;
|
||||
}
|
||||
|
||||
killLocalVoxels();
|
||||
|
||||
// handle "file://" urls...
|
||||
if (url.isLocalFile()) {
|
||||
QString pathString = url.path();
|
||||
QByteArray pathAsAscii = pathString.toAscii();
|
||||
const char* path = pathAsAscii.data();
|
||||
readFromSVOFile(path);
|
||||
return;
|
||||
}
|
||||
|
||||
// load the URL data asynchronously
|
||||
if (!url.isValid()) {
|
||||
return;
|
||||
}
|
||||
_voxelReply = Application::getInstance()->getNetworkAccessManager()->get(QNetworkRequest(url));
|
||||
connect(_voxelReply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleVoxelDownloadProgress(qint64,qint64)));
|
||||
connect(_voxelReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleVoxelReplyError()));
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||
float voxelScale, const nodeColor& color) {
|
||||
VoxelSystem::updateNodeInArrays(nodeIndex, startVertex, voxelScale, color);
|
||||
|
||||
GLubyte* writeBoneIndicesAt = _writeBoneIndicesArray + (nodeIndex * BONE_ELEMENTS_PER_VOXEL);
|
||||
GLfloat* writeBoneWeightsAt = _writeBoneWeightsArray + (nodeIndex * BONE_ELEMENTS_PER_VOXEL);
|
||||
for (int i = 0; i < VERTICES_PER_VOXEL; i++) {
|
||||
BoneIndices boneIndices;
|
||||
glm::vec4 boneWeights;
|
||||
computeBoneIndicesAndWeights(computeVoxelVertex(startVertex, voxelScale, i), boneIndices, boneWeights);
|
||||
for (int j = 0; j < BONE_ELEMENTS_PER_VERTEX; j++) {
|
||||
*(writeBoneIndicesAt + i * BONE_ELEMENTS_PER_VERTEX + j) = boneIndices[j];
|
||||
*(writeBoneWeightsAt + i * BONE_ELEMENTS_PER_VERTEX + j) = boneWeights[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
|
||||
VoxelSystem::copyWrittenDataSegmentToReadArrays(segmentStart, segmentEnd);
|
||||
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readBoneIndicesAt = _readBoneIndicesArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
GLubyte* writeBoneIndicesAt = _writeBoneIndicesArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
memcpy(readBoneIndicesAt, writeBoneIndicesAt, segmentSizeBytes);
|
||||
|
||||
segmentStartAt = segmentStart * BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat);
|
||||
segmentSizeBytes = segmentLength * BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readBoneWeightsAt = _readBoneWeightsArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
GLfloat* writeBoneWeightsAt = _writeBoneWeightsArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
memcpy(readBoneWeightsAt, writeBoneWeightsAt, segmentSizeBytes);
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
|
||||
VoxelSystem::updateVBOSegment(segmentStart, segmentEnd);
|
||||
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readBoneIndicesFrom = _readBoneIndicesArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneIndicesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readBoneIndicesFrom);
|
||||
|
||||
segmentStartAt = segmentStart * BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat);
|
||||
segmentSizeBytes = segmentLength * BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readBoneWeightsFrom = _readBoneWeightsArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneWeightsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readBoneWeightsFrom);
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::applyScaleAndBindProgram(bool texture) {
|
||||
_skinProgram->bind();
|
||||
|
||||
// the base matrix includes centering and scale
|
||||
QMatrix4x4 baseMatrix;
|
||||
baseMatrix.scale(_treeScale);
|
||||
baseMatrix.translate(-0.5f, -0.5f, -0.5f);
|
||||
|
||||
// bone matrices include joint transforms
|
||||
QMatrix4x4 boneMatrices[NUM_AVATAR_JOINTS];
|
||||
for (int i = 0; i < NUM_AVATAR_JOINTS; i++) {
|
||||
glm::vec3 position;
|
||||
glm::quat orientation;
|
||||
_avatar->getBodyBallTransform((AvatarJointID)i, position, orientation);
|
||||
boneMatrices[i].translate(position.x, position.y, position.z);
|
||||
orientation = orientation * glm::inverse(_avatar->getSkeleton().joint[i].absoluteBindPoseRotation);
|
||||
boneMatrices[i].rotate(QQuaternion(orientation.w, orientation.x, orientation.y, orientation.z));
|
||||
const glm::vec3& bindPosition = _avatar->getSkeleton().joint[i].absoluteBindPosePosition;
|
||||
boneMatrices[i].translate(-bindPosition.x, -bindPosition.y, -bindPosition.z);
|
||||
boneMatrices[i] *= baseMatrix;
|
||||
}
|
||||
_skinProgram->setUniformValueArray(_boneMatricesLocation, boneMatrices, NUM_AVATAR_JOINTS);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneIndicesID);
|
||||
glVertexAttribPointer(_boneIndicesLocation, BONE_ELEMENTS_PER_VERTEX, GL_UNSIGNED_BYTE, false, 0, 0);
|
||||
_skinProgram->enableAttributeArray(_boneIndicesLocation);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneWeightsID);
|
||||
_skinProgram->setAttributeBuffer(_boneWeightsLocation, GL_FLOAT, 0, BONE_ELEMENTS_PER_VERTEX);
|
||||
_skinProgram->enableAttributeArray(_boneWeightsLocation);
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::removeScaleAndReleaseProgram(bool texture) {
|
||||
_skinProgram->release();
|
||||
_skinProgram->disableAttributeArray(_boneIndicesLocation);
|
||||
_skinProgram->disableAttributeArray(_boneWeightsLocation);
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::handleVoxelDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
||||
// for now, just wait until we have the full business
|
||||
if (bytesReceived < bytesTotal) {
|
||||
return;
|
||||
}
|
||||
|
||||
// XXXBHG - I don't know why this can happen, but when I was testing this, I used a dropbox URL
|
||||
// and it resulted in a case where the bytesTotal == bytesReceived, but the _voxelReply was NULL
|
||||
// which needless to say caused crashes below. I've added this quick guard to protect against
|
||||
// this case, but it probably should be investigated.
|
||||
if (!_voxelReply) {
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray entirety = _voxelReply->readAll();
|
||||
_voxelReply->deleteLater();
|
||||
_voxelReply = 0;
|
||||
_tree->readBitstreamToTree((unsigned char*)entirety.data(), entirety.size(), WANT_COLOR, NO_EXISTS_BITS);
|
||||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::handleVoxelReplyError() {
|
||||
printLog("%s\n", _voxelReply->errorString().toAscii().constData());
|
||||
|
||||
_voxelReply->deleteLater();
|
||||
_voxelReply = 0;
|
||||
}
|
||||
|
||||
class IndexDistance {
|
||||
public:
|
||||
IndexDistance(GLubyte index = AVATAR_JOINT_PELVIS, float distance = FLT_MAX) : index(index), distance(distance) { }
|
||||
|
||||
GLubyte index;
|
||||
float distance;
|
||||
};
|
||||
|
||||
void AvatarVoxelSystem::computeBoneIndicesAndWeights(const glm::vec3& vertex, BoneIndices& indices, glm::vec4& weights) const {
|
||||
// transform into joint space
|
||||
glm::vec3 jointVertex = (vertex - glm::vec3(0.5f, 0.5f, 0.5f)) * AVATAR_TREE_SCALE;
|
||||
|
||||
// find the nearest four joints (TODO: use a better data structure for the pose positions to speed this up)
|
||||
IndexDistance nearest[BONE_ELEMENTS_PER_VERTEX];
|
||||
const Skeleton& skeleton = _avatar->getSkeleton();
|
||||
for (int i = 0; i < NUM_AVATAR_JOINTS; i++) {
|
||||
AvatarJointID parent = skeleton.joint[i].parent;
|
||||
float distance = glm::length(computeVectorFromPointToSegment(jointVertex,
|
||||
skeleton.joint[parent == AVATAR_JOINT_NULL ? i : parent].absoluteBindPosePosition,
|
||||
skeleton.joint[i].absoluteBindPosePosition));
|
||||
if (distance > skeleton.joint[i].bindRadius) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < BONE_ELEMENTS_PER_VERTEX; j++) {
|
||||
if (distance < nearest[j].distance) {
|
||||
// move the rest of the indices down
|
||||
for (int k = BONE_ELEMENTS_PER_VERTEX - 1; k > j; k--) {
|
||||
nearest[k] = nearest[k - 1];
|
||||
}
|
||||
nearest[j] = IndexDistance(i, distance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compute the weights based on inverse distance
|
||||
float totalWeight = 0.0f;
|
||||
for (int i = 0; i < BONE_ELEMENTS_PER_VERTEX; i++) {
|
||||
indices[i] = nearest[i].index;
|
||||
if (nearest[i].distance != FLT_MAX) {
|
||||
weights[i] = 1.0f / glm::max(nearest[i].distance, EPSILON);
|
||||
totalWeight += weights[i];
|
||||
|
||||
} else {
|
||||
weights[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// if it's not attached to anything, consider it attached to the hip
|
||||
if (totalWeight == 0.0f) {
|
||||
weights[0] = 1.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
// ortherwise, normalize the weights
|
||||
for (int i = 0; i < BONE_ELEMENTS_PER_VERTEX; i++) {
|
||||
weights[i] /= totalWeight;
|
||||
}
|
||||
}
|
74
interface/src/AvatarVoxelSystem.h
Normal file
74
interface/src/AvatarVoxelSystem.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// AvatarVoxelSystem.h
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/31/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__AvatarVoxelSystem__
|
||||
#define __interface__AvatarVoxelSystem__
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "VoxelSystem.h"
|
||||
|
||||
const int BONE_ELEMENTS_PER_VERTEX = 4;
|
||||
typedef GLubyte BoneIndices[BONE_ELEMENTS_PER_VERTEX];
|
||||
|
||||
class QNetworkReply;
|
||||
class QUrl;
|
||||
|
||||
class Avatar;
|
||||
|
||||
class AvatarVoxelSystem : public QObject, public VoxelSystem {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AvatarVoxelSystem(Avatar* avatar);
|
||||
virtual ~AvatarVoxelSystem();
|
||||
|
||||
virtual void init();
|
||||
|
||||
virtual void removeOutOfView();
|
||||
|
||||
void loadVoxelsFromURL(const QUrl& url);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||
float voxelScale, const nodeColor& color);
|
||||
virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||
virtual void updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||
virtual void applyScaleAndBindProgram(bool texture);
|
||||
virtual void removeScaleAndReleaseProgram(bool texture);
|
||||
|
||||
private slots:
|
||||
|
||||
void handleVoxelDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||
void handleVoxelReplyError();
|
||||
|
||||
private:
|
||||
|
||||
void computeBoneIndicesAndWeights(const glm::vec3& vertex, BoneIndices& indices, glm::vec4& weights) const;
|
||||
|
||||
Avatar* _avatar;
|
||||
|
||||
GLubyte* _readBoneIndicesArray;
|
||||
GLfloat* _readBoneWeightsArray;
|
||||
GLubyte* _writeBoneIndicesArray;
|
||||
GLfloat* _writeBoneWeightsArray;
|
||||
|
||||
GLuint _vboBoneIndicesID;
|
||||
GLuint _vboBoneWeightsID;
|
||||
|
||||
QNetworkReply* _voxelReply;
|
||||
|
||||
static ProgramObject* _skinProgram;
|
||||
static int _boneMatricesLocation;
|
||||
static int _boneIndicesLocation;
|
||||
static int _boneWeightsLocation;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__AvatarVoxelSystem__) */
|
|
@ -171,9 +171,9 @@ void Head::determineIfLookingAtSomething() {
|
|||
void Head::calculateGeometry() {
|
||||
//generate orientation directions
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 right = orientation * AVATAR_RIGHT;
|
||||
glm::vec3 up = orientation * AVATAR_UP;
|
||||
glm::vec3 front = orientation * AVATAR_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
|
||||
//calculate the eye positions
|
||||
_leftEyePosition = _position
|
||||
|
@ -346,9 +346,9 @@ void Head::renderMouth() {
|
|||
float s = sqrt(_averageLoudness);
|
||||
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 right = orientation * AVATAR_RIGHT;
|
||||
glm::vec3 up = orientation * AVATAR_UP;
|
||||
glm::vec3 front = orientation * AVATAR_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
|
||||
glm::vec3 r = right * _scale * (0.30f + s * 0.0014f );
|
||||
glm::vec3 u = up * _scale * (0.05f + s * 0.0040f );
|
||||
|
@ -414,9 +414,9 @@ void Head::renderEyeBrows() {
|
|||
glm::vec3 rightBottom = _leftEyePosition;
|
||||
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 right = orientation * AVATAR_RIGHT;
|
||||
glm::vec3 up = orientation * AVATAR_UP;
|
||||
glm::vec3 front = orientation * AVATAR_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
|
||||
glm::vec3 r = right * length;
|
||||
glm::vec3 u = up * height;
|
||||
|
@ -501,20 +501,20 @@ void Head::renderEyeBalls() {
|
|||
|
||||
//rotate the eyeball to aim towards the lookat position
|
||||
glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - _leftEyePosition); // the lookat direction
|
||||
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, AVATAR_UP);
|
||||
float angle = 180.0f - angleBetween(targetLookatAxis, AVATAR_UP);
|
||||
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP);
|
||||
float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP);
|
||||
glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
|
||||
glRotatef(180.0, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations
|
||||
} else {
|
||||
|
||||
//rotate the eyeball to aim straight ahead
|
||||
glm::vec3 rotationAxisToHeadFront = glm::cross(front, AVATAR_UP);
|
||||
float angleToHeadFront = 180.0f - angleBetween(front, AVATAR_UP);
|
||||
glm::vec3 rotationAxisToHeadFront = glm::cross(front, IDENTITY_UP);
|
||||
float angleToHeadFront = 180.0f - angleBetween(front, IDENTITY_UP);
|
||||
glRotatef(angleToHeadFront, rotationAxisToHeadFront.x, rotationAxisToHeadFront.y, rotationAxisToHeadFront.z);
|
||||
|
||||
//set the amount of roll (for correction after previous rotations)
|
||||
float rollRotation = angleBetween(front, AVATAR_FRONT);
|
||||
float dot = glm::dot(front, -AVATAR_RIGHT);
|
||||
float rollRotation = angleBetween(front, IDENTITY_FRONT);
|
||||
float dot = glm::dot(front, -IDENTITY_RIGHT);
|
||||
if ( dot < 0.0f ) { rollRotation = -rollRotation; }
|
||||
glRotatef(rollRotation, 0.0f, 1.0f, 0.0f); //roll the iris or correct roll about the lookat vector
|
||||
}
|
||||
|
@ -545,21 +545,21 @@ void Head::renderEyeBalls() {
|
|||
|
||||
//rotate the eyeball to aim towards the lookat position
|
||||
glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - _rightEyePosition);
|
||||
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, AVATAR_UP);
|
||||
float angle = 180.0f - angleBetween(targetLookatAxis, AVATAR_UP);
|
||||
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP);
|
||||
float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP);
|
||||
glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
|
||||
glRotatef(180.0f, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations
|
||||
|
||||
} else {
|
||||
|
||||
//rotate the eyeball to aim straight ahead
|
||||
glm::vec3 rotationAxisToHeadFront = glm::cross(front, AVATAR_UP);
|
||||
float angleToHeadFront = 180.0f - angleBetween(front, AVATAR_UP);
|
||||
glm::vec3 rotationAxisToHeadFront = glm::cross(front, IDENTITY_UP);
|
||||
float angleToHeadFront = 180.0f - angleBetween(front, IDENTITY_UP);
|
||||
glRotatef(angleToHeadFront, rotationAxisToHeadFront.x, rotationAxisToHeadFront.y, rotationAxisToHeadFront.z);
|
||||
|
||||
//set the amount of roll (for correction after previous rotations)
|
||||
float rollRotation = angleBetween(front, AVATAR_FRONT);
|
||||
float dot = glm::dot(front, -AVATAR_RIGHT);
|
||||
float rollRotation = angleBetween(front, IDENTITY_FRONT);
|
||||
float dot = glm::dot(front, -IDENTITY_RIGHT);
|
||||
if ( dot < 0.0f ) { rollRotation = -rollRotation; }
|
||||
glRotatef(rollRotation, 0.0f, 1.0f, 0.0f); //roll the iris or correct roll about the lookat vector
|
||||
}
|
||||
|
@ -595,9 +595,8 @@ void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosi
|
|||
void Head::updateHairPhysics(float deltaTime) {
|
||||
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 right = orientation * AVATAR_RIGHT;
|
||||
glm::vec3 up = orientation * AVATAR_UP;
|
||||
glm::vec3 front = orientation * AVATAR_FRONT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
|
||||
for (int t = 0; t < NUM_HAIR_TUFTS; t ++) {
|
||||
|
||||
|
|
|
@ -49,9 +49,9 @@ public:
|
|||
glm::quat getOrientation() const;
|
||||
glm::quat getWorldAlignedOrientation () const;
|
||||
|
||||
glm::vec3 getRightDirection() const { return getOrientation() * AVATAR_RIGHT; }
|
||||
glm::vec3 getUpDirection () const { return getOrientation() * AVATAR_UP; }
|
||||
glm::vec3 getFrontDirection() const { return getOrientation() * AVATAR_FRONT; }
|
||||
glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getUpDirection () const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||
|
||||
const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
|
||||
float getAverageLoudness() {return _averageLoudness;};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
|
||||
#include "Skeleton.h"
|
||||
#include "Util.h"
|
||||
|
||||
const float BODY_SPRING_DEFAULT_TIGHTNESS = 1000.0f;
|
||||
const float FLOATING_HEIGHT = 0.13f;
|
||||
|
@ -14,12 +15,13 @@ Skeleton::Skeleton() {
|
|||
|
||||
void Skeleton::initialize() {
|
||||
|
||||
for (int b=0; b<NUM_AVATAR_JOINTS; b++) {
|
||||
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) {
|
||||
joint[b].parent = AVATAR_JOINT_NULL;
|
||||
joint[b].position = glm::vec3(0.0, 0.0, 0.0);
|
||||
joint[b].defaultPosePosition = glm::vec3(0.0, 0.0, 0.0);
|
||||
joint[b].rotation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
joint[b].rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
joint[b].length = 0.0;
|
||||
joint[b].bindRadius = 1.0f / 8;
|
||||
}
|
||||
|
||||
// specify the parental hierarchy
|
||||
|
@ -48,6 +50,35 @@ void Skeleton::initialize() {
|
|||
joint[ AVATAR_JOINT_RIGHT_HEEL ].parent = AVATAR_JOINT_RIGHT_KNEE;
|
||||
joint[ AVATAR_JOINT_RIGHT_TOES ].parent = AVATAR_JOINT_RIGHT_HEEL;
|
||||
|
||||
// specify the bind pose position
|
||||
joint[ AVATAR_JOINT_PELVIS ].bindPosePosition = glm::vec3( 0.0, 0.0, 0.0 );
|
||||
joint[ AVATAR_JOINT_TORSO ].bindPosePosition = glm::vec3( 0.0, 0.09, -0.01 );
|
||||
joint[ AVATAR_JOINT_CHEST ].bindPosePosition = glm::vec3( 0.0, 0.09, -0.01 );
|
||||
joint[ AVATAR_JOINT_NECK_BASE ].bindPosePosition = glm::vec3( 0.0, 0.14, 0.01 );
|
||||
joint[ AVATAR_JOINT_HEAD_BASE ].bindPosePosition = glm::vec3( 0.0, 0.04, 0.00 );
|
||||
|
||||
joint[ AVATAR_JOINT_LEFT_COLLAR ].bindPosePosition = glm::vec3( -0.06, 0.04, 0.01 );
|
||||
joint[ AVATAR_JOINT_LEFT_SHOULDER ].bindPosePosition = glm::vec3( -0.05, 0.0, 0.01 );
|
||||
joint[ AVATAR_JOINT_LEFT_ELBOW ].bindPosePosition = glm::vec3( -0.16, 0.0, 0.0 );
|
||||
joint[ AVATAR_JOINT_LEFT_WRIST ].bindPosePosition = glm::vec3( -0.12, 0.0, 0.0 );
|
||||
joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].bindPosePosition = glm::vec3( -0.1, 0.0, 0.0 );
|
||||
|
||||
joint[ AVATAR_JOINT_RIGHT_COLLAR ].bindPosePosition = glm::vec3( 0.06, 0.04, 0.01 );
|
||||
joint[ AVATAR_JOINT_RIGHT_SHOULDER ].bindPosePosition = glm::vec3( 0.05, 0.0, 0.01 );
|
||||
joint[ AVATAR_JOINT_RIGHT_ELBOW ].bindPosePosition = glm::vec3( 0.16, 0.0, 0.0 );
|
||||
joint[ AVATAR_JOINT_RIGHT_WRIST ].bindPosePosition = glm::vec3( 0.12, 0.0, 0.0 );
|
||||
joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].bindPosePosition = glm::vec3( 0.1, 0.0, 0.0 );
|
||||
|
||||
joint[ AVATAR_JOINT_LEFT_HIP ].bindPosePosition = glm::vec3( -0.05, 0.0, 0.02 );
|
||||
joint[ AVATAR_JOINT_LEFT_KNEE ].bindPosePosition = glm::vec3( 0.00, -0.25, 0.00 );
|
||||
joint[ AVATAR_JOINT_LEFT_HEEL ].bindPosePosition = glm::vec3( 0.00, -0.23, 0.00 );
|
||||
joint[ AVATAR_JOINT_LEFT_TOES ].bindPosePosition = glm::vec3( 0.00, 0.00, -0.06 );
|
||||
|
||||
joint[ AVATAR_JOINT_RIGHT_HIP ].bindPosePosition = glm::vec3( 0.05, 0.0, 0.02 );
|
||||
joint[ AVATAR_JOINT_RIGHT_KNEE ].bindPosePosition = glm::vec3( 0.00, -0.25, 0.00 );
|
||||
joint[ AVATAR_JOINT_RIGHT_HEEL ].bindPosePosition = glm::vec3( 0.00, -0.23, 0.00 );
|
||||
joint[ AVATAR_JOINT_RIGHT_TOES ].bindPosePosition = glm::vec3( 0.00, 0.00, -0.06 );
|
||||
|
||||
// specify the default pose position
|
||||
joint[ AVATAR_JOINT_PELVIS ].defaultPosePosition = glm::vec3( 0.0, 0.0, 0.0 );
|
||||
joint[ AVATAR_JOINT_TORSO ].defaultPosePosition = glm::vec3( 0.0, 0.09, -0.01 );
|
||||
|
@ -77,9 +108,18 @@ void Skeleton::initialize() {
|
|||
joint[ AVATAR_JOINT_RIGHT_HEEL ].defaultPosePosition = glm::vec3( -0.01, -0.22, 0.08 );
|
||||
joint[ AVATAR_JOINT_RIGHT_TOES ].defaultPosePosition = glm::vec3( 0.00, -0.03, -0.05 );
|
||||
|
||||
// calculate bone length
|
||||
// calculate bone length, absolute bind positions/rotations
|
||||
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) {
|
||||
joint[b].length = glm::length(joint[b].defaultPosePosition);
|
||||
|
||||
if (joint[b].parent == AVATAR_JOINT_NULL) {
|
||||
joint[b].absoluteBindPosePosition = joint[b].bindPosePosition;
|
||||
joint[b].absoluteBindPoseRotation = glm::quat();
|
||||
} else {
|
||||
joint[b].absoluteBindPosePosition = joint[ joint[b].parent ].absoluteBindPosePosition +
|
||||
joint[b].bindPosePosition;
|
||||
joint[b].absoluteBindPoseRotation = rotationBetween(JOINT_DIRECTION, joint[b].bindPosePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,6 +141,7 @@ void Skeleton::update(float deltaTime, const glm::quat& orientation, glm::vec3 p
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
float Skeleton::getArmLength() {
|
||||
return joint[ AVATAR_JOINT_RIGHT_ELBOW ].length
|
||||
+ joint[ AVATAR_JOINT_RIGHT_WRIST ].length
|
||||
|
|
|
@ -13,35 +13,36 @@
|
|||
|
||||
enum AvatarJointID
|
||||
{
|
||||
AVATAR_JOINT_NULL = -1,
|
||||
AVATAR_JOINT_PELVIS,
|
||||
AVATAR_JOINT_TORSO,
|
||||
AVATAR_JOINT_CHEST,
|
||||
AVATAR_JOINT_NECK_BASE,
|
||||
AVATAR_JOINT_HEAD_BASE,
|
||||
AVATAR_JOINT_HEAD_TOP,
|
||||
AVATAR_JOINT_LEFT_COLLAR,
|
||||
AVATAR_JOINT_LEFT_SHOULDER,
|
||||
AVATAR_JOINT_LEFT_ELBOW,
|
||||
AVATAR_JOINT_LEFT_WRIST,
|
||||
AVATAR_JOINT_LEFT_FINGERTIPS,
|
||||
AVATAR_JOINT_RIGHT_COLLAR,
|
||||
AVATAR_JOINT_RIGHT_SHOULDER,
|
||||
AVATAR_JOINT_RIGHT_ELBOW,
|
||||
AVATAR_JOINT_RIGHT_WRIST,
|
||||
AVATAR_JOINT_RIGHT_FINGERTIPS,
|
||||
AVATAR_JOINT_LEFT_HIP,
|
||||
AVATAR_JOINT_LEFT_KNEE,
|
||||
AVATAR_JOINT_LEFT_HEEL,
|
||||
AVATAR_JOINT_LEFT_TOES,
|
||||
AVATAR_JOINT_RIGHT_HIP,
|
||||
AVATAR_JOINT_RIGHT_KNEE,
|
||||
AVATAR_JOINT_RIGHT_HEEL,
|
||||
AVATAR_JOINT_RIGHT_TOES,
|
||||
AVATAR_JOINT_NULL = -1,
|
||||
AVATAR_JOINT_PELVIS,
|
||||
AVATAR_JOINT_TORSO,
|
||||
AVATAR_JOINT_CHEST,
|
||||
AVATAR_JOINT_NECK_BASE,
|
||||
AVATAR_JOINT_HEAD_BASE,
|
||||
AVATAR_JOINT_HEAD_TOP,
|
||||
AVATAR_JOINT_LEFT_COLLAR,
|
||||
AVATAR_JOINT_LEFT_SHOULDER,
|
||||
AVATAR_JOINT_LEFT_ELBOW,
|
||||
AVATAR_JOINT_LEFT_WRIST,
|
||||
AVATAR_JOINT_LEFT_FINGERTIPS,
|
||||
AVATAR_JOINT_RIGHT_COLLAR,
|
||||
AVATAR_JOINT_RIGHT_SHOULDER,
|
||||
AVATAR_JOINT_RIGHT_ELBOW,
|
||||
AVATAR_JOINT_RIGHT_WRIST,
|
||||
AVATAR_JOINT_RIGHT_FINGERTIPS,
|
||||
AVATAR_JOINT_LEFT_HIP,
|
||||
AVATAR_JOINT_LEFT_KNEE,
|
||||
AVATAR_JOINT_LEFT_HEEL,
|
||||
AVATAR_JOINT_LEFT_TOES,
|
||||
AVATAR_JOINT_RIGHT_HIP,
|
||||
AVATAR_JOINT_RIGHT_KNEE,
|
||||
AVATAR_JOINT_RIGHT_HEEL,
|
||||
AVATAR_JOINT_RIGHT_TOES,
|
||||
|
||||
NUM_AVATAR_JOINTS
|
||||
NUM_AVATAR_JOINTS
|
||||
};
|
||||
|
||||
const glm::vec3 JOINT_DIRECTION = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
|
||||
class Skeleton {
|
||||
public:
|
||||
|
@ -55,17 +56,22 @@ public:
|
|||
float getHeight();
|
||||
float getPelvisStandingHeight();
|
||||
float getPelvisFloatingHeight();
|
||||
//glm::vec3 getJointVectorFromParent(AvatarJointID jointID) {return joint[jointID].position - joint[joint[jointID].parent].position; }
|
||||
|
||||
struct AvatarJoint
|
||||
{
|
||||
AvatarJointID parent; // which joint is this joint connected to?
|
||||
glm::vec3 position; // the position at the "end" of the joint - in global space
|
||||
glm::vec3 defaultPosePosition; // the parent relative position when the avatar is in the "T-pose"
|
||||
glm::quat rotation; // the parent-relative rotation (orientation) of the joint as a quaternion
|
||||
float length; // the length of vector connecting the joint and its parent
|
||||
AvatarJointID parent; // which joint is this joint connected to?
|
||||
glm::vec3 position; // the position at the "end" of the joint - in global space
|
||||
glm::vec3 defaultPosePosition; // the parent relative position when the avatar is in the default pose
|
||||
glm::vec3 bindPosePosition; // the parent relative position when the avatar is in the "T-pose"
|
||||
glm::vec3 absoluteBindPosePosition; // the absolute position when the avatar is in the "T-pose"
|
||||
glm::quat absoluteBindPoseRotation; // the absolute rotation when the avatar is in the "T-pose"
|
||||
float bindRadius; // the radius of the bone capsule that envelops the vertices to bind
|
||||
glm::quat rotation; // the parent-relative rotation (orientation) of the joint as a quaternion
|
||||
float length; // the length of vector connecting the joint and its parent
|
||||
};
|
||||
|
||||
AvatarJoint joint[ NUM_AVATAR_JOINTS ];
|
||||
AvatarJoint joint[ NUM_AVATAR_JOINTS ];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "world.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include "VoxelConstants.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// no clue which versions are affected...
|
||||
|
@ -72,6 +74,27 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2) {
|
|||
return acos((glm::dot(v1, v2)) / (glm::length(v1) * glm::length(v2))) * 180.f / PIf;
|
||||
}
|
||||
|
||||
// Helper function return the rotation from the first vector onto the second
|
||||
glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
|
||||
float angle = angleBetween(v1, v2);
|
||||
if (isnan(angle) || angle < EPSILON) {
|
||||
return glm::quat();
|
||||
}
|
||||
glm::vec3 axis = glm::cross(v1, v2);
|
||||
if (angle > 179.99f) { // 180 degree rotation; must use another axis
|
||||
axis = glm::cross(v1, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
float axisLength = glm::length(axis);
|
||||
if (axisLength < EPSILON) { // parallel to x; y will work
|
||||
axis = glm::normalize(glm::cross(v1, glm::vec3(0.0f, 1.0f, 0.0f)));
|
||||
} else {
|
||||
axis /= axisLength;
|
||||
}
|
||||
} else {
|
||||
axis = glm::normalize(glm::cross(v1, v2));
|
||||
}
|
||||
return glm::angleAxis(angle, axis);
|
||||
}
|
||||
|
||||
// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's
|
||||
// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde,
|
||||
// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
|
||||
|
@ -394,9 +417,9 @@ void renderCircle(glm::vec3 position, float radius, glm::vec3 surfaceNormal, int
|
|||
|
||||
|
||||
void renderOrientationDirections(glm::vec3 position, const glm::quat& orientation, float size) {
|
||||
glm::vec3 pRight = position + orientation * AVATAR_RIGHT * size;
|
||||
glm::vec3 pUp = position + orientation * AVATAR_UP * size;
|
||||
glm::vec3 pFront = position + orientation * AVATAR_FRONT * size;
|
||||
glm::vec3 pRight = position + orientation * IDENTITY_RIGHT * size;
|
||||
glm::vec3 pUp = position + orientation * IDENTITY_UP * size;
|
||||
glm::vec3 pFront = position + orientation * IDENTITY_FRONT * size;
|
||||
|
||||
glColor3f(1.0f, 0.0f, 0.0f);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
|
|
|
@ -45,6 +45,8 @@ void drawVector(glm::vec3* vector);
|
|||
|
||||
float angleBetween(const glm::vec3& v1, const glm::vec3& v2);
|
||||
|
||||
glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2);
|
||||
|
||||
glm::vec3 safeEulerAngles(const glm::quat& q);
|
||||
|
||||
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <PerfStat.h>
|
||||
#include <OctalCode.h>
|
||||
#include <pthread.h>
|
||||
#include "Application.h"
|
||||
#include "Log.h"
|
||||
#include "VoxelConstants.h"
|
||||
#include "InterfaceConfig.h"
|
||||
|
@ -37,14 +38,15 @@ GLfloat identityNormals[] = { 0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1,
|
|||
-1,0,0, +1,0,0, +1,0,0, -1,0,0,
|
||||
-1,0,0, +1,0,0, +1,0,0, -1,0,0 };
|
||||
|
||||
GLubyte identityIndices[] = { 0,2,1, 0,3,2, // Z- .
|
||||
GLubyte identityIndices[] = { 0,2,1, 0,3,2, // Z-
|
||||
8,9,13, 8,13,12, // Y-
|
||||
16,23,19, 16,20,23, // X-
|
||||
17,18,22, 17,22,21, // X+
|
||||
10,11,15, 10,15,14, // Y+
|
||||
4,5,6, 4,6,7 }; // Z+ .
|
||||
4,5,6, 4,6,7 }; // Z+
|
||||
|
||||
VoxelSystem::VoxelSystem() : AgentData(NULL) {
|
||||
VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) :
|
||||
AgentData(NULL), _treeScale(treeScale), _maxVoxels(maxVoxels) {
|
||||
_voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0;
|
||||
_writeRenderFullVBO = true;
|
||||
_readRenderFullVBO = true;
|
||||
|
@ -70,6 +72,18 @@ void VoxelSystem::loadVoxelsFile(const char* fileName, bool wantColorRandomizer)
|
|||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
|
||||
void VoxelSystem::writeToSVOFile(const char* filename, VoxelNode* node) const {
|
||||
_tree->writeToSVOFile(filename, node);
|
||||
}
|
||||
|
||||
bool VoxelSystem::readFromSVOFile(const char* filename) {
|
||||
bool result = _tree->readFromSVOFile(filename);
|
||||
if (result) {
|
||||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
long int VoxelSystem::getVoxelsCreated() {
|
||||
return _tree->voxelsCreated;
|
||||
}
|
||||
|
@ -229,10 +243,8 @@ void VoxelSystem::cleanupRemovedVoxels() {
|
|||
}
|
||||
|
||||
void VoxelSystem::copyWrittenDataToReadArraysFullVBOs() {
|
||||
int bytesOfVertices = (_voxelsInWriteArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLfloat);
|
||||
int bytesOfColors = (_voxelsInWriteArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLubyte);
|
||||
memcpy(_readVerticesArray, _writeVerticesArray, bytesOfVertices);
|
||||
memcpy(_readColorsArray, _writeColorsArray, bytesOfColors );
|
||||
copyWrittenDataSegmentToReadArrays(0, _voxelsInWriteArrays - 1);
|
||||
|
||||
_voxelsInReadArrays = _voxelsInWriteArrays;
|
||||
|
||||
// clear our dirty flags
|
||||
|
@ -259,47 +271,37 @@ void VoxelSystem::copyWrittenDataToReadArraysPartialVBOs() {
|
|||
if (!thisVoxelDirty) {
|
||||
// If we got here because because this voxel is NOT dirty, so the last dirty voxel was the one before
|
||||
// this one and so that's where the "segment" ends
|
||||
segmentEnd = i - 1;
|
||||
copyWrittenDataSegmentToReadArrays(segmentStart, i - 1);
|
||||
inSegment = false;
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesAt = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
GLfloat* writeVerticesAt = _writeVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
memcpy(readVerticesAt, writeVerticesAt, segmentSizeBytes);
|
||||
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsAt = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
GLubyte* writeColorsAt = _writeColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
memcpy(readColorsAt, writeColorsAt, segmentSizeBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we got to the end of the array, and we're in an active dirty segment...
|
||||
if (inSegment) {
|
||||
segmentEnd = _voxelsInWriteArrays - 1;
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesAt = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
GLfloat* writeVerticesAt = _writeVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
memcpy(readVerticesAt, writeVerticesAt, segmentSizeBytes);
|
||||
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsAt = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
GLubyte* writeColorsAt = _writeColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
memcpy(readColorsAt, writeColorsAt, segmentSizeBytes);
|
||||
copyWrittenDataSegmentToReadArrays(segmentStart, _voxelsInWriteArrays - 1);
|
||||
}
|
||||
|
||||
// update our length
|
||||
_voxelsInReadArrays = _voxelsInWriteArrays;
|
||||
}
|
||||
|
||||
void VoxelSystem::copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesAt = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
GLfloat* writeVerticesAt = _writeVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
memcpy(readVerticesAt, writeVerticesAt, segmentSizeBytes);
|
||||
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsAt = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
GLubyte* writeColorsAt = _writeColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
memcpy(readColorsAt, writeColorsAt, segmentSizeBytes);
|
||||
}
|
||||
|
||||
void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) {
|
||||
PerformanceWarning warn(_renderWarningsOn, "copyWrittenDataToReadArrays()");
|
||||
if (_voxelsDirty && _voxelsUpdated) {
|
||||
|
@ -312,12 +314,11 @@ void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) {
|
|||
}
|
||||
|
||||
int VoxelSystem::newTreeToArrays(VoxelNode* node) {
|
||||
assert(_viewFrustum); // you must set up _viewFrustum before calling this
|
||||
int voxelsUpdated = 0;
|
||||
bool shouldRender = false; // assume we don't need to render it
|
||||
// if it's colored, we might need to render it!
|
||||
if (node->isColored()) {
|
||||
float distanceToNode = node->distanceToCamera(*_viewFrustum);
|
||||
float distanceToNode = node->distanceToCamera(*Application::getInstance()->getViewFrustum());
|
||||
float boundary = boundaryDistanceForRenderLevel(node->getLevel());
|
||||
float childBoundary = boundaryDistanceForRenderLevel(node->getLevel() + 1);
|
||||
bool inBoundary = (distanceToNode <= boundary);
|
||||
|
@ -352,7 +353,7 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
|
|||
|
||||
int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) {
|
||||
// If we've run out of room, then just bail...
|
||||
if (_voxelsInWriteArrays >= MAX_VOXELS_PER_SYSTEM) {
|
||||
if (_voxelsInWriteArrays >= _maxVoxels) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -363,12 +364,7 @@ int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) {
|
|||
|
||||
// populate the array with points for the 8 vertices
|
||||
// and RGB color for each added vertex
|
||||
for (int j = 0; j < VERTEX_POINTS_PER_VOXEL; j++ ) {
|
||||
GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
|
||||
GLubyte* writeColorsAt = _writeColorsArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
|
||||
*(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale);
|
||||
*(writeColorsAt +j) = node->getColor()[j % 3];
|
||||
}
|
||||
updateNodeInArrays(nodeIndex, startVertex, voxelScale, node->getColor());
|
||||
node->setBufferIndex(nodeIndex);
|
||||
_writeVoxelDirtyArray[nodeIndex] = true; // just in case we switch to Partial mode
|
||||
_voxelsInWriteArrays++; // our know vertices in the arrays
|
||||
|
@ -382,7 +378,7 @@ int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) {
|
|||
|
||||
int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
|
||||
// If we've run out of room, then just bail...
|
||||
if (_voxelsInWriteArrays >= MAX_VOXELS_PER_SYSTEM) {
|
||||
if (_voxelsInWriteArrays >= _maxVoxels) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -414,17 +410,31 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
|
|||
|
||||
// populate the array with points for the 8 vertices
|
||||
// and RGB color for each added vertex
|
||||
for (int j = 0; j < VERTEX_POINTS_PER_VOXEL; j++ ) {
|
||||
GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
|
||||
GLubyte* writeColorsAt = _writeColorsArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
|
||||
*(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale);
|
||||
*(writeColorsAt +j) = node->getColor()[j % 3];
|
||||
}
|
||||
updateNodeInArrays(nodeIndex, startVertex, voxelScale, node->getColor());
|
||||
|
||||
return 1; // updated!
|
||||
}
|
||||
return 0; // not-updated
|
||||
}
|
||||
|
||||
void VoxelSystem::updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||
float voxelScale, const nodeColor& color) {
|
||||
for (int j = 0; j < VERTEX_POINTS_PER_VOXEL; j++ ) {
|
||||
GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
|
||||
GLubyte* writeColorsAt = _writeColorsArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
|
||||
*(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale);
|
||||
*(writeColorsAt +j) = color[j % 3];
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 VoxelSystem::computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const {
|
||||
const float* identityVertex = identityVertices + index * 3;
|
||||
return startVertex + glm::vec3(identityVertex[0], identityVertex[1], identityVertex[2]) * voxelScale;
|
||||
}
|
||||
|
||||
ProgramObject* VoxelSystem::_perlinModulateProgram = 0;
|
||||
GLuint VoxelSystem::_permutationNormalTextureID = 0;
|
||||
|
||||
void VoxelSystem::init() {
|
||||
|
||||
_renderWarningsOn = false;
|
||||
|
@ -440,23 +450,23 @@ void VoxelSystem::init() {
|
|||
_unusedArraySpace = 0;
|
||||
|
||||
// we will track individual dirty sections with these arrays of bools
|
||||
_writeVoxelDirtyArray = new bool[MAX_VOXELS_PER_SYSTEM];
|
||||
memset(_writeVoxelDirtyArray, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool));
|
||||
_readVoxelDirtyArray = new bool[MAX_VOXELS_PER_SYSTEM];
|
||||
memset(_readVoxelDirtyArray, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool));
|
||||
_writeVoxelDirtyArray = new bool[_maxVoxels];
|
||||
memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool));
|
||||
_readVoxelDirtyArray = new bool[_maxVoxels];
|
||||
memset(_readVoxelDirtyArray, false, _maxVoxels * sizeof(bool));
|
||||
|
||||
// prep the data structures for incoming voxel data
|
||||
_writeVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM];
|
||||
_readVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM];
|
||||
_writeVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
|
||||
_readVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
|
||||
|
||||
_writeColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM];
|
||||
_readColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM];
|
||||
_writeColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
|
||||
_readColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
|
||||
|
||||
GLuint* indicesArray = new GLuint[INDICES_PER_VOXEL * MAX_VOXELS_PER_SYSTEM];
|
||||
GLuint* indicesArray = new GLuint[INDICES_PER_VOXEL * _maxVoxels];
|
||||
|
||||
// populate the indicesArray
|
||||
// this will not change given new voxels, so we can set it all up now
|
||||
for (int n = 0; n < MAX_VOXELS_PER_SYSTEM; n++) {
|
||||
for (int n = 0; n < _maxVoxels; n++) {
|
||||
// fill the indices array
|
||||
int voxelIndexOffset = n * INDICES_PER_VOXEL;
|
||||
GLuint* currentIndicesPos = indicesArray + voxelIndexOffset;
|
||||
|
@ -468,11 +478,11 @@ void VoxelSystem::init() {
|
|||
}
|
||||
}
|
||||
|
||||
GLfloat* normalsArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM];
|
||||
GLfloat* normalsArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
|
||||
GLfloat* normalsArrayEndPointer = normalsArray;
|
||||
|
||||
// populate the normalsArray
|
||||
for (int n = 0; n < MAX_VOXELS_PER_SYSTEM; n++) {
|
||||
for (int n = 0; n < _maxVoxels; n++) {
|
||||
for (int i = 0; i < VERTEX_POINTS_PER_VOXEL; i++) {
|
||||
*(normalsArrayEndPointer++) = identityNormals[i];
|
||||
}
|
||||
|
@ -481,32 +491,35 @@ void VoxelSystem::init() {
|
|||
// VBO for the verticesArray
|
||||
glGenBuffers(1, &_vboVerticesID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * MAX_VOXELS_PER_SYSTEM, NULL, GL_DYNAMIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);
|
||||
|
||||
// VBO for the normalsArray
|
||||
glGenBuffers(1, &_vboNormalsID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * MAX_VOXELS_PER_SYSTEM,
|
||||
VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels,
|
||||
normalsArray, GL_STATIC_DRAW);
|
||||
|
||||
// VBO for colorsArray
|
||||
glGenBuffers(1, &_vboColorsID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte) * MAX_VOXELS_PER_SYSTEM, NULL, GL_DYNAMIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);
|
||||
|
||||
// VBO for the indicesArray
|
||||
glGenBuffers(1, &_vboIndicesID);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
||||
INDICES_PER_VOXEL * sizeof(GLuint) * MAX_VOXELS_PER_SYSTEM,
|
||||
INDICES_PER_VOXEL * sizeof(GLuint) * _maxVoxels,
|
||||
indicesArray, GL_STATIC_DRAW);
|
||||
|
||||
// delete the indices and normals arrays that are no longer needed
|
||||
delete[] indicesArray;
|
||||
delete[] normalsArray;
|
||||
|
||||
// create our simple fragment shader
|
||||
// create our simple fragment shader if we're the first system to init
|
||||
if (_perlinModulateProgram != 0) {
|
||||
return;
|
||||
}
|
||||
switchToResourcesParentIfRequired();
|
||||
_perlinModulateProgram = new ProgramObject();
|
||||
_perlinModulateProgram->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/perlin_modulate.vert");
|
||||
|
@ -539,20 +552,7 @@ void VoxelSystem::init() {
|
|||
}
|
||||
|
||||
void VoxelSystem::updateFullVBOs() {
|
||||
glBufferIndex segmentStart = 0;
|
||||
glBufferIndex segmentEnd = _voxelsInReadArrays;
|
||||
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
|
||||
updateVBOSegment(0, _voxelsInReadArrays);
|
||||
|
||||
// consider the _readVoxelDirtyArray[] clean!
|
||||
memset(_readVoxelDirtyArray, false, _voxelsInReadArrays * sizeof(bool));
|
||||
|
@ -574,39 +574,17 @@ void VoxelSystem::updatePartialVBOs() {
|
|||
if (!thisVoxelDirty) {
|
||||
// If we got here because because this voxel is NOT dirty, so the last dirty voxel was the one before
|
||||
// this one and so that's where the "segment" ends
|
||||
segmentEnd = i - 1;
|
||||
updateVBOSegment(segmentStart, i - 1);
|
||||
inSegment = false;
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
|
||||
}
|
||||
_readVoxelDirtyArray[i] = false; // consider us clean!
|
||||
}
|
||||
}
|
||||
|
||||
// if we got to the end of the array, and we're in an active dirty segment...
|
||||
if (inSegment) {
|
||||
segmentEnd = _voxelsInReadArrays - 1;
|
||||
if (inSegment) {
|
||||
updateVBOSegment(segmentStart, _voxelsInReadArrays - 1);
|
||||
inSegment = false;
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -628,14 +606,28 @@ void VoxelSystem::updateVBOs() {
|
|||
_callsToTreesToArrays = 0; // clear it
|
||||
}
|
||||
|
||||
void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
|
||||
}
|
||||
|
||||
void VoxelSystem::render(bool texture) {
|
||||
PerformanceWarning warn(_renderWarningsOn, "render()");
|
||||
|
||||
// get the lock so that the update thread won't change anything
|
||||
pthread_mutex_lock(&_bufferWriteLock);
|
||||
|
||||
glPushMatrix();
|
||||
updateVBOs();
|
||||
|
||||
// tell OpenGL where to find vertex and color information
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
@ -650,10 +642,7 @@ void VoxelSystem::render(bool texture) {
|
|||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0);
|
||||
|
||||
if (texture) {
|
||||
_perlinModulateProgram->bind();
|
||||
glBindTexture(GL_TEXTURE_2D, _permutationNormalTextureID);
|
||||
}
|
||||
applyScaleAndBindProgram(texture);
|
||||
|
||||
// for performance, disable blending and enable backface culling
|
||||
glDisable(GL_BLEND);
|
||||
|
@ -661,17 +650,13 @@ void VoxelSystem::render(bool texture) {
|
|||
|
||||
// draw the number of voxels we have
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID);
|
||||
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
|
||||
glDrawRangeElementsEXT(GL_TRIANGLES, 0, VERTICES_PER_VOXEL * _voxelsInReadArrays - 1,
|
||||
36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
if (texture) {
|
||||
_perlinModulateProgram->release();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
removeScaleAndReleaseProgram(texture);
|
||||
|
||||
// deactivate vertex and color arrays after drawing
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
@ -681,11 +666,28 @@ void VoxelSystem::render(bool texture) {
|
|||
// bind with 0 to switch back to normal operation
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
pthread_mutex_unlock(&_bufferWriteLock);
|
||||
}
|
||||
|
||||
void VoxelSystem::applyScaleAndBindProgram(bool texture) {
|
||||
glPushMatrix();
|
||||
glScalef(_treeScale, _treeScale, _treeScale);
|
||||
|
||||
if (texture) {
|
||||
_perlinModulateProgram->bind();
|
||||
glBindTexture(GL_TEXTURE_2D, _permutationNormalTextureID);
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelSystem::removeScaleAndReleaseProgram(bool texture) {
|
||||
// scale back down to 1 so heads aren't massive
|
||||
glPopMatrix();
|
||||
|
||||
pthread_mutex_unlock(&_bufferWriteLock);
|
||||
if (texture) {
|
||||
_perlinModulateProgram->release();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int VoxelSystem::_nodeCount = 0;
|
||||
|
@ -855,7 +857,7 @@ bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) {
|
|||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
VoxelNode* childNode = node->getChildAtIndex(i);
|
||||
if (childNode) {
|
||||
ViewFrustum::location inFrustum = childNode->inFrustum(*thisVoxelSystem->_viewFrustum);
|
||||
ViewFrustum::location inFrustum = childNode->inFrustum(*Application::getInstance()->getViewFrustum());
|
||||
switch (inFrustum) {
|
||||
case ViewFrustum::OUTSIDE: {
|
||||
args->nodesOutside++;
|
||||
|
@ -907,9 +909,9 @@ bool VoxelSystem::hasViewChanged() {
|
|||
}
|
||||
|
||||
// If our viewFrustum has changed since our _lastKnowViewFrustum
|
||||
if (_viewFrustum && !_lastStableViewFrustum.matches(_viewFrustum)) {
|
||||
if (!_lastStableViewFrustum.matches(Application::getInstance()->getViewFrustum())) {
|
||||
result = true;
|
||||
_lastStableViewFrustum = *_viewFrustum; // save last stable
|
||||
_lastStableViewFrustum = *Application::getInstance()->getViewFrustum(); // save last stable
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -1147,3 +1149,12 @@ void VoxelSystem::createSphere(float r,float xc, float yc, float zc, float s, bo
|
|||
_tree->createSphere(r, xc, yc, zc, s, solid, mode, destructive, debug);
|
||||
setupNewVoxelsForDrawing();
|
||||
};
|
||||
|
||||
void VoxelSystem::copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinationTree, bool rebaseToRoot) {
|
||||
_tree->copySubTreeIntoNewTree(startNode, destinationTree, rebaseToRoot);
|
||||
}
|
||||
|
||||
void VoxelSystem::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode) {
|
||||
_tree->copyFromTreeIntoSubTree(sourceTree, destinationNode);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <AgentData.h>
|
||||
#include <VoxelTree.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include "Avatar.h"
|
||||
#include "Camera.h"
|
||||
#include "Util.h"
|
||||
#include "world.h"
|
||||
|
@ -27,23 +26,21 @@ const int NUM_CHILDREN = 8;
|
|||
|
||||
class VoxelSystem : public AgentData {
|
||||
public:
|
||||
VoxelSystem();
|
||||
VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = MAX_VOXELS_PER_SYSTEM);
|
||||
~VoxelSystem();
|
||||
|
||||
int parseData(unsigned char* sourceBuffer, int numBytes);
|
||||
|
||||
void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; };
|
||||
|
||||
void init();
|
||||
virtual void init();
|
||||
void simulate(float deltaTime) { };
|
||||
void render(bool texture);
|
||||
|
||||
unsigned long getVoxelsUpdated() const {return _voxelsUpdated;};
|
||||
unsigned long getVoxelsRendered() const {return _voxelsInReadArrays;};
|
||||
|
||||
void setViewerAvatar(Avatar *newViewerAvatar) { _viewerAvatar = newViewerAvatar; };
|
||||
void setCamera(Camera* newCamera) { _camera = newCamera; };
|
||||
void loadVoxelsFile(const char* fileName,bool wantColorRandomizer);
|
||||
void writeToSVOFile(const char* filename, VoxelNode* node) const;
|
||||
bool readFromSVOFile(const char* filename);
|
||||
|
||||
long int getVoxelsCreated();
|
||||
long int getVoxelsColored();
|
||||
|
@ -64,7 +61,7 @@ public:
|
|||
void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; };
|
||||
bool getRenderPipelineWarnings() const { return _renderWarningsOn; };
|
||||
|
||||
void removeOutOfView();
|
||||
virtual void removeOutOfView();
|
||||
bool hasViewChanged();
|
||||
bool isViewChanging();
|
||||
|
||||
|
@ -83,6 +80,26 @@ public:
|
|||
void createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color, bool destructive = false);
|
||||
void createSphere(float r,float xc, float yc, float zc, float s, bool solid,
|
||||
creationMode mode, bool destructive = false, bool debug = false);
|
||||
|
||||
void copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinationTree, bool rebaseToRoot);
|
||||
void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode);
|
||||
|
||||
protected:
|
||||
float _treeScale;
|
||||
int _maxVoxels;
|
||||
VoxelTree* _tree;
|
||||
|
||||
glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const;
|
||||
|
||||
void setupNewVoxelsForDrawing();
|
||||
|
||||
virtual void updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||
float voxelScale, const nodeColor& color);
|
||||
virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||
virtual void updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||
virtual void applyScaleAndBindProgram(bool texture);
|
||||
virtual void removeScaleAndReleaseProgram(bool texture);
|
||||
|
||||
private:
|
||||
// disallow copying of VoxelSystem objects
|
||||
VoxelSystem(const VoxelSystem&);
|
||||
|
@ -110,13 +127,12 @@ private:
|
|||
void copyWrittenDataToReadArraysFullVBOs();
|
||||
void copyWrittenDataToReadArraysPartialVBOs();
|
||||
|
||||
void updateVBOs();
|
||||
|
||||
// these are kinda hacks, used by getDistanceFromViewRangeOperation() probably shouldn't be here
|
||||
static float _maxDistance;
|
||||
static float _minDistance;
|
||||
|
||||
Avatar* _viewerAvatar;
|
||||
Camera* _camera;
|
||||
VoxelTree* _tree;
|
||||
GLfloat* _readVerticesArray;
|
||||
GLubyte* _readColorsArray;
|
||||
GLfloat* _writeVerticesArray;
|
||||
|
@ -124,8 +140,8 @@ private:
|
|||
bool* _writeVoxelDirtyArray;
|
||||
bool* _readVoxelDirtyArray;
|
||||
unsigned long _voxelsUpdated;
|
||||
unsigned long _voxelsInWriteArrays;
|
||||
unsigned long _voxelsInReadArrays;
|
||||
unsigned long _voxelsInWriteArrays;
|
||||
unsigned long _unusedArraySpace;
|
||||
|
||||
bool _writeRenderFullVBO;
|
||||
|
@ -143,26 +159,21 @@ private:
|
|||
pthread_mutex_t _bufferWriteLock;
|
||||
pthread_mutex_t _treeLock;
|
||||
|
||||
ProgramObject* _perlinModulateProgram;
|
||||
GLuint _permutationNormalTextureID;
|
||||
|
||||
ViewFrustum* _viewFrustum;
|
||||
ViewFrustum _lastKnowViewFrustum;
|
||||
ViewFrustum _lastStableViewFrustum;
|
||||
|
||||
int newTreeToArrays(VoxelNode *currentNode);
|
||||
void cleanupRemovedVoxels();
|
||||
|
||||
void setupNewVoxelsForDrawing();
|
||||
void copyWrittenDataToReadArrays(bool fullVBOs);
|
||||
|
||||
void updateFullVBOs(); // all voxels in the VBO
|
||||
void updatePartialVBOs(); // multiple segments, only dirty voxels
|
||||
|
||||
bool _voxelsDirty;
|
||||
|
||||
public:
|
||||
void updateVBOs();
|
||||
void updateFullVBOs(); // all voxels in the VBO
|
||||
void updatePartialVBOs(); // multiple segments, only dirty voxels
|
||||
|
||||
static ProgramObject* _perlinModulateProgram;
|
||||
static GLuint _permutationNormalTextureID;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
#include "Application.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <OctalCode.h>
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
|
||||
timeval startup_time;
|
||||
gettimeofday(&startup_time, NULL);
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@ void* AudioInjectionManager::injectAudioViaThread(void* args) {
|
|||
// if we don't have an explicit destination socket then pull active socket for current audio mixer from agent list
|
||||
if (!_isDestinationSocketExplicit) {
|
||||
Agent* audioMixer = AgentList::getInstance()->soloAgentOfType(AGENT_TYPE_AUDIO_MIXER);
|
||||
|
||||
if (audioMixer) {
|
||||
_destinationSocket = *audioMixer->getActiveSocket();
|
||||
}
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
const int MAX_INJECTOR_VOLUME = 0xFF;
|
||||
|
||||
AudioInjector::AudioInjector(const char* filename) :
|
||||
_position(),
|
||||
_bearing(0),
|
||||
_position(0.0f, 0.0f, 0.0f),
|
||||
_orientation(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
_radius(0.0f),
|
||||
_volume(MAX_INJECTOR_VOLUME),
|
||||
_indexOfNextSlot(0),
|
||||
_isInjectingAudio(false)
|
||||
|
@ -47,8 +48,9 @@ AudioInjector::AudioInjector(const char* filename) :
|
|||
|
||||
AudioInjector::AudioInjector(int maxNumSamples) :
|
||||
_numTotalSamples(maxNumSamples),
|
||||
_position(),
|
||||
_bearing(0),
|
||||
_position(0.0f, 0.0f, 0.0f),
|
||||
_orientation(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
_radius(0.0f),
|
||||
_volume(MAX_INJECTOR_VOLUME),
|
||||
_indexOfNextSlot(0),
|
||||
_isInjectingAudio(false)
|
||||
|
@ -70,12 +72,20 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination
|
|||
timeval startTime;
|
||||
|
||||
// calculate the number of bytes required for additional data
|
||||
int leadingBytes = sizeof(PACKET_HEADER) + sizeof(_streamIdentifier)
|
||||
+ sizeof(_position) + sizeof(_bearing) + sizeof(_volume);
|
||||
int leadingBytes = sizeof(PACKET_HEADER) + sizeof(INJECT_AUDIO_AT_POINT_COMMAND) + sizeof(_streamIdentifier)
|
||||
+ sizeof(_position) + sizeof(_orientation) + sizeof(_volume);
|
||||
|
||||
if (_radius > 0) {
|
||||
// we'll need 4 extra bytes if the cube side length is being sent as well
|
||||
leadingBytes += sizeof(_radius);
|
||||
}
|
||||
|
||||
unsigned char dataPacket[BUFFER_LENGTH_BYTES + leadingBytes];
|
||||
|
||||
dataPacket[0] = PACKET_HEADER_INJECT_AUDIO;
|
||||
unsigned char *currentPacketPtr = dataPacket + 1;
|
||||
// add the correct command for point source or cube of sound
|
||||
dataPacket[1] = (_radius > 0) ? INJECT_AUDIO_AT_CUBE_COMMAND : INJECT_AUDIO_AT_POINT_COMMAND;
|
||||
unsigned char *currentPacketPtr = dataPacket + sizeof(PACKET_HEADER) + sizeof(INJECT_AUDIO_AT_POINT_COMMAND);
|
||||
|
||||
// copy the identifier for this injector
|
||||
memcpy(currentPacketPtr, &_streamIdentifier, sizeof(_streamIdentifier));
|
||||
|
@ -84,11 +94,18 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination
|
|||
memcpy(currentPacketPtr, &_position, sizeof(_position));
|
||||
currentPacketPtr += sizeof(_position);
|
||||
|
||||
if (_radius > 0) {
|
||||
// if we have a cube half height we need to send it here
|
||||
// this tells the mixer how much volume the injected audio will occupy
|
||||
memcpy(currentPacketPtr, &_radius, sizeof(_radius));
|
||||
currentPacketPtr += sizeof(_radius);
|
||||
}
|
||||
|
||||
*currentPacketPtr = _volume;
|
||||
currentPacketPtr++;
|
||||
|
||||
memcpy(currentPacketPtr, &_bearing, sizeof(_bearing));
|
||||
currentPacketPtr += sizeof(_bearing);
|
||||
memcpy(currentPacketPtr, &_orientation, sizeof(_orientation));
|
||||
currentPacketPtr += sizeof(_orientation);
|
||||
|
||||
gettimeofday(&startTime, NULL);
|
||||
int nextFrame = 0;
|
||||
|
|
|
@ -36,8 +36,11 @@ public:
|
|||
const glm::vec3& getPosition() const { return _position; }
|
||||
void setPosition(const glm::vec3& position) { _position = position; }
|
||||
|
||||
float getBearing() const { return _bearing; }
|
||||
void setBearing(float bearing) { _bearing = bearing; }
|
||||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
void setOrientation(const glm::quat& orientation) { _orientation = orientation; }
|
||||
|
||||
float getRadius() const { return _radius; }
|
||||
void setRadius(float radius) { _radius = radius; }
|
||||
|
||||
void addSample(const int16_t sample);
|
||||
void addSamples(int16_t* sampleBuffer, int numSamples);
|
||||
|
@ -46,7 +49,8 @@ private:
|
|||
int16_t* _audioSampleArray;
|
||||
int _numTotalSamples;
|
||||
glm::vec3 _position;
|
||||
float _bearing;
|
||||
glm::quat _orientation;
|
||||
float _radius;
|
||||
unsigned char _volume;
|
||||
int _indexOfNextSlot;
|
||||
bool _isInjectingAudio;
|
||||
|
|
|
@ -17,6 +17,7 @@ AudioRingBuffer::AudioRingBuffer(int ringSamples, int bufferSamples) :
|
|||
AgentData(NULL),
|
||||
_ringBufferLengthSamples(ringSamples),
|
||||
_bufferLengthSamples(bufferSamples),
|
||||
_radius(0.0f),
|
||||
_endOfLastWrite(NULL),
|
||||
_started(false),
|
||||
_shouldBeAddedToMix(false),
|
||||
|
@ -46,31 +47,34 @@ int AudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
// we've got a stream identifier to pull from the packet
|
||||
memcpy(&_streamIdentifier, dataBuffer, sizeof(_streamIdentifier));
|
||||
dataBuffer += sizeof(_streamIdentifier);
|
||||
|
||||
// push past the injection command
|
||||
dataBuffer += sizeof(INJECT_AUDIO_AT_POINT_COMMAND);
|
||||
}
|
||||
|
||||
memcpy(&_position, dataBuffer, sizeof(_position));
|
||||
dataBuffer += (sizeof(_position));
|
||||
dataBuffer += sizeof(_position);
|
||||
|
||||
if (sourceBuffer[0] == PACKET_HEADER_INJECT_AUDIO && sourceBuffer[1] == INJECT_AUDIO_AT_CUBE_COMMAND) {
|
||||
// this is audio that needs to be injected as a volume (cube)
|
||||
// parse out the cubeHalfHeight sent by the client
|
||||
memcpy(&_radius, dataBuffer, sizeof(_radius));
|
||||
dataBuffer += sizeof(_radius);
|
||||
}
|
||||
|
||||
unsigned int attenuationByte = *(dataBuffer++);
|
||||
_attenuationRatio = attenuationByte / 255.0f;
|
||||
|
||||
memcpy(&_bearing, dataBuffer, sizeof(float));
|
||||
dataBuffer += sizeof(_bearing);
|
||||
memcpy(&_orientation, dataBuffer, sizeof(_orientation));
|
||||
dataBuffer += sizeof(_orientation);
|
||||
|
||||
// if this agent sent us a NaN bearing then don't consider this good audio and bail
|
||||
if (std::isnan(_bearing)) {
|
||||
// if this agent sent us a NaN for first float in orientation then don't consider this good audio and bail
|
||||
if (std::isnan(_orientation.x)) {
|
||||
_endOfLastWrite = _nextOutput = _buffer;
|
||||
_started = false;
|
||||
return 0;
|
||||
} else if (_bearing > 180 || _bearing < -180) {
|
||||
// we were passed an invalid bearing because this agent wants loopback (pressed the H key)
|
||||
_shouldLoopbackForAgent = true;
|
||||
|
||||
// correct the bearing
|
||||
_bearing = _bearing > 0
|
||||
? _bearing - AGENT_LOOPBACK_MODIFIER
|
||||
: _bearing + AGENT_LOOPBACK_MODIFIER;
|
||||
} else {
|
||||
// currently no possiblity for loopback, need to add once quaternion audio is working again
|
||||
_shouldLoopbackForAgent = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,12 +21,17 @@
|
|||
const int STREAM_IDENTIFIER_NUM_BYTES = 8;
|
||||
typedef std::map<uint16_t, stk::FreeVerb*> FreeVerbAgentMap;
|
||||
|
||||
const char INJECT_AUDIO_AT_POINT_COMMAND = 'P';
|
||||
const char INJECT_AUDIO_AT_CUBE_COMMAND = 'C';
|
||||
|
||||
class AudioRingBuffer : public AgentData {
|
||||
public:
|
||||
AudioRingBuffer(int ringSamples, int bufferSamples);
|
||||
~AudioRingBuffer();
|
||||
|
||||
int parseData(unsigned char* sourceBuffer, int numBytes);
|
||||
|
||||
float getRadius() const { return _radius; }
|
||||
|
||||
int16_t* getNextOutput() const { return _nextOutput; }
|
||||
void setNextOutput(int16_t* nextOutput) { _nextOutput = nextOutput; }
|
||||
|
@ -45,8 +50,9 @@ public:
|
|||
void setShouldBeAddedToMix(bool shouldBeAddedToMix) { _shouldBeAddedToMix = shouldBeAddedToMix; }
|
||||
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
float getAttenuationRatio() const { return _attenuationRatio; }
|
||||
float getBearing() const { return _bearing; }
|
||||
|
||||
bool shouldLoopbackForAgent() const { return _shouldLoopbackForAgent; }
|
||||
const unsigned char* getStreamIdentifier() const { return _streamIdentifier; }
|
||||
|
||||
|
@ -59,8 +65,9 @@ private:
|
|||
int _ringBufferLengthSamples;
|
||||
int _bufferLengthSamples;
|
||||
glm::vec3 _position;
|
||||
glm::quat _orientation;
|
||||
float _radius;
|
||||
float _attenuationRatio;
|
||||
float _bearing;
|
||||
int16_t* _nextOutput;
|
||||
int16_t* _endOfLastWrite;
|
||||
int16_t* _buffer;
|
||||
|
|
|
@ -15,4 +15,7 @@ include(${MACRO_DIR}/IncludeGLM.cmake)
|
|||
include_glm(${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
# link in the hifi voxels library
|
||||
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
|
||||
|
|
|
@ -14,23 +14,10 @@
|
|||
#include <PacketHeaders.h>
|
||||
|
||||
#include "AvatarData.h"
|
||||
#include <VoxelConstants.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int packFloatAngleToTwoByte(unsigned char* buffer, float angle) {
|
||||
const float ANGLE_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 360.0);
|
||||
|
||||
uint16_t angleHolder = floorf((angle + 180) * ANGLE_CONVERSION_RATIO);
|
||||
memcpy(buffer, &angleHolder, sizeof(uint16_t));
|
||||
|
||||
return sizeof(uint16_t);
|
||||
}
|
||||
|
||||
int unpackFloatAngleFromTwoByte(uint16_t* byteAnglePointer, float* destinationPointer) {
|
||||
*destinationPointer = (*byteAnglePointer / (float) std::numeric_limits<uint16_t>::max()) * 360.0 - 180;
|
||||
return sizeof(uint16_t);
|
||||
}
|
||||
|
||||
AvatarData::AvatarData(Agent* owningAgent) :
|
||||
AgentData(owningAgent),
|
||||
_handPosition(0,0,0),
|
||||
|
@ -39,9 +26,7 @@ AvatarData::AvatarData(Agent* owningAgent) :
|
|||
_bodyRoll(0.0),
|
||||
_handState(0),
|
||||
_cameraPosition(0,0,0),
|
||||
_cameraDirection(0,0,0),
|
||||
_cameraUp(0,0,0),
|
||||
_cameraRight(0,0,0),
|
||||
_cameraOrientation(),
|
||||
_cameraFov(0.0f),
|
||||
_cameraAspectRatio(0.0f),
|
||||
_cameraNearClip(0.0f),
|
||||
|
@ -91,55 +76,45 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
|
|||
memcpy(destinationBuffer, &_headData->_leanForward, sizeof(_headData->_leanForward));
|
||||
destinationBuffer += sizeof(_headData->_leanForward);
|
||||
|
||||
// Hand Position
|
||||
memcpy(destinationBuffer, &_handPosition, sizeof(float) * 3);
|
||||
// Hand Position - is relative to body position
|
||||
glm::vec3 handPositionRelative = _handPosition - _position;
|
||||
memcpy(destinationBuffer, &handPositionRelative, sizeof(float) * 3);
|
||||
destinationBuffer += sizeof(float) * 3;
|
||||
|
||||
// Lookat Position
|
||||
memcpy(destinationBuffer, &_headData->_lookAtPosition, sizeof(_headData->_lookAtPosition));
|
||||
destinationBuffer += sizeof(_headData->_lookAtPosition);
|
||||
|
||||
// Hand State (0 = not grabbing, 1 = grabbing)
|
||||
memcpy(destinationBuffer, &_handState, sizeof(char));
|
||||
destinationBuffer += sizeof(char);
|
||||
|
||||
// Instantaneous audio loudness (used to drive facial animation)
|
||||
//destinationBuffer += packFloatToByte(destinationBuffer, std::min(MAX_AUDIO_LOUDNESS, _audioLoudness), MAX_AUDIO_LOUDNESS);
|
||||
memcpy(destinationBuffer, &_headData->_audioLoudness, sizeof(float));
|
||||
destinationBuffer += sizeof(float);
|
||||
|
||||
// camera details
|
||||
memcpy(destinationBuffer, &_cameraPosition, sizeof(_cameraPosition));
|
||||
destinationBuffer += sizeof(_cameraPosition);
|
||||
memcpy(destinationBuffer, &_cameraDirection, sizeof(_cameraDirection));
|
||||
destinationBuffer += sizeof(_cameraDirection);
|
||||
memcpy(destinationBuffer, &_cameraRight, sizeof(_cameraRight));
|
||||
destinationBuffer += sizeof(_cameraRight);
|
||||
memcpy(destinationBuffer, &_cameraUp, sizeof(_cameraUp));
|
||||
destinationBuffer += sizeof(_cameraUp);
|
||||
memcpy(destinationBuffer, &_cameraFov, sizeof(_cameraFov));
|
||||
destinationBuffer += sizeof(_cameraFov);
|
||||
memcpy(destinationBuffer, &_cameraAspectRatio, sizeof(_cameraAspectRatio));
|
||||
destinationBuffer += sizeof(_cameraAspectRatio);
|
||||
memcpy(destinationBuffer, &_cameraNearClip, sizeof(_cameraNearClip));
|
||||
destinationBuffer += sizeof(_cameraNearClip);
|
||||
memcpy(destinationBuffer, &_cameraFarClip, sizeof(_cameraFarClip));
|
||||
destinationBuffer += sizeof(_cameraFarClip);
|
||||
|
||||
// key state
|
||||
*destinationBuffer++ = _keyState;
|
||||
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _cameraOrientation);
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _cameraFov);
|
||||
destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _cameraAspectRatio);
|
||||
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraNearClip);
|
||||
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraFarClip);
|
||||
|
||||
// chat message
|
||||
*destinationBuffer++ = _chatMessage.size();
|
||||
memcpy(destinationBuffer, _chatMessage.data(), _chatMessage.size() * sizeof(char));
|
||||
destinationBuffer += _chatMessage.size() * sizeof(char);
|
||||
|
||||
// voxel sending features...
|
||||
// voxel sending features...
|
||||
unsigned char wantItems = 0;
|
||||
if (_wantResIn) { setAtBit(wantItems,WANT_RESIN_AT_BIT); }
|
||||
if (_wantColor) { setAtBit(wantItems,WANT_COLOR_AT_BIT); }
|
||||
if (_wantDelta) { setAtBit(wantItems,WANT_DELTA_AT_BIT); }
|
||||
*destinationBuffer++ = wantItems;
|
||||
// bitMask of less than byte wide items
|
||||
unsigned char bitItems = 0;
|
||||
if (_wantResIn) { setAtBit(bitItems,WANT_RESIN_AT_BIT); }
|
||||
if (_wantColor) { setAtBit(bitItems,WANT_COLOR_AT_BIT); }
|
||||
if (_wantDelta) { setAtBit(bitItems,WANT_DELTA_AT_BIT); }
|
||||
|
||||
// key state
|
||||
setSemiNibbleAt(bitItems,KEY_STATE_START_BIT,_keyState);
|
||||
// hand state
|
||||
setSemiNibbleAt(bitItems,HAND_STATE_START_BIT,_handState);
|
||||
*destinationBuffer++ = bitItems;
|
||||
|
||||
return destinationBuffer - bufferStart;
|
||||
}
|
||||
|
@ -185,54 +160,170 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
memcpy(&_headData->_leanForward, sourceBuffer, sizeof(_headData->_leanForward));
|
||||
sourceBuffer += sizeof(_headData->_leanForward);
|
||||
|
||||
// Hand Position
|
||||
memcpy(&_handPosition, sourceBuffer, sizeof(float) * 3);
|
||||
// Hand Position - is relative to body position
|
||||
glm::vec3 handPositionRelative;
|
||||
memcpy(&handPositionRelative, sourceBuffer, sizeof(float) * 3);
|
||||
_handPosition = _position + handPositionRelative;
|
||||
sourceBuffer += sizeof(float) * 3;
|
||||
|
||||
// Lookat Position
|
||||
memcpy(&_headData->_lookAtPosition, sourceBuffer, sizeof(_headData->_lookAtPosition));
|
||||
sourceBuffer += sizeof(_headData->_lookAtPosition);
|
||||
|
||||
// Hand State
|
||||
memcpy(&_handState, sourceBuffer, sizeof(char));
|
||||
sourceBuffer += sizeof(char);
|
||||
|
||||
|
||||
// Instantaneous audio loudness (used to drive facial animation)
|
||||
//sourceBuffer += unpackFloatFromByte(sourceBuffer, _audioLoudness, MAX_AUDIO_LOUDNESS);
|
||||
memcpy(&_headData->_audioLoudness, sourceBuffer, sizeof(float));
|
||||
sourceBuffer += sizeof(float);
|
||||
|
||||
// camera details
|
||||
memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition));
|
||||
sourceBuffer += sizeof(_cameraPosition);
|
||||
memcpy(&_cameraDirection, sourceBuffer, sizeof(_cameraDirection));
|
||||
sourceBuffer += sizeof(_cameraDirection);
|
||||
memcpy(&_cameraRight, sourceBuffer, sizeof(_cameraRight));
|
||||
sourceBuffer += sizeof(_cameraRight);
|
||||
memcpy(&_cameraUp, sourceBuffer, sizeof(_cameraUp));
|
||||
sourceBuffer += sizeof(_cameraUp);
|
||||
memcpy(&_cameraFov, sourceBuffer, sizeof(_cameraFov));
|
||||
sourceBuffer += sizeof(_cameraFov);
|
||||
memcpy(&_cameraAspectRatio, sourceBuffer, sizeof(_cameraAspectRatio));
|
||||
sourceBuffer += sizeof(_cameraAspectRatio);
|
||||
memcpy(&_cameraNearClip, sourceBuffer, sizeof(_cameraNearClip));
|
||||
sourceBuffer += sizeof(_cameraNearClip);
|
||||
memcpy(&_cameraFarClip, sourceBuffer, sizeof(_cameraFarClip));
|
||||
sourceBuffer += sizeof(_cameraFarClip);
|
||||
|
||||
// key state
|
||||
_keyState = (KeyState)*sourceBuffer++;
|
||||
|
||||
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, _cameraOrientation);
|
||||
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_cameraFov);
|
||||
sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer,_cameraAspectRatio);
|
||||
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraNearClip);
|
||||
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraFarClip);
|
||||
|
||||
// the rest is a chat message
|
||||
int chatMessageSize = *sourceBuffer++;
|
||||
_chatMessage = string((char*)sourceBuffer, chatMessageSize);
|
||||
sourceBuffer += chatMessageSize * sizeof(char);
|
||||
|
||||
// voxel sending features...
|
||||
unsigned char wantItems = 0;
|
||||
wantItems = (unsigned char)*sourceBuffer++;
|
||||
_wantResIn = oneAtBit(wantItems,WANT_RESIN_AT_BIT);
|
||||
_wantColor = oneAtBit(wantItems,WANT_COLOR_AT_BIT);
|
||||
_wantDelta = oneAtBit(wantItems,WANT_DELTA_AT_BIT);
|
||||
unsigned char bitItems = 0;
|
||||
bitItems = (unsigned char)*sourceBuffer++;
|
||||
_wantResIn = oneAtBit(bitItems,WANT_RESIN_AT_BIT);
|
||||
_wantColor = oneAtBit(bitItems,WANT_COLOR_AT_BIT);
|
||||
_wantDelta = oneAtBit(bitItems,WANT_DELTA_AT_BIT);
|
||||
|
||||
// key state, stored as a semi-nibble in the bitItems
|
||||
_keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT);
|
||||
|
||||
// hand state, stored as a semi-nibble in the bitItems
|
||||
_handState = getSemiNibbleAt(bitItems,HAND_STATE_START_BIT);
|
||||
|
||||
return sourceBuffer - startPosition;
|
||||
}
|
||||
|
||||
glm::vec3 AvatarData::calculateCameraDirection() const {
|
||||
glm::vec3 direction = glm::vec3(_cameraOrientation * glm::vec4(IDENTITY_FRONT, 0.0f));
|
||||
return direction;
|
||||
}
|
||||
|
||||
|
||||
int packFloatAngleToTwoByte(unsigned char* buffer, float angle) {
|
||||
const float ANGLE_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 360.0);
|
||||
|
||||
uint16_t angleHolder = floorf((angle + 180) * ANGLE_CONVERSION_RATIO);
|
||||
memcpy(buffer, &angleHolder, sizeof(uint16_t));
|
||||
|
||||
return sizeof(uint16_t);
|
||||
}
|
||||
|
||||
int unpackFloatAngleFromTwoByte(uint16_t* byteAnglePointer, float* destinationPointer) {
|
||||
*destinationPointer = (*byteAnglePointer / (float) std::numeric_limits<uint16_t>::max()) * 360.0 - 180;
|
||||
return sizeof(uint16_t);
|
||||
}
|
||||
|
||||
int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput) {
|
||||
const float QUAT_PART_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 2.0);
|
||||
uint16_t quatParts[4];
|
||||
quatParts[0] = floorf((quatInput.x + 1.0) * QUAT_PART_CONVERSION_RATIO);
|
||||
quatParts[1] = floorf((quatInput.y + 1.0) * QUAT_PART_CONVERSION_RATIO);
|
||||
quatParts[2] = floorf((quatInput.z + 1.0) * QUAT_PART_CONVERSION_RATIO);
|
||||
quatParts[3] = floorf((quatInput.w + 1.0) * QUAT_PART_CONVERSION_RATIO);
|
||||
|
||||
memcpy(buffer, &quatParts, sizeof(quatParts));
|
||||
return sizeof(quatParts);
|
||||
}
|
||||
|
||||
int unpackOrientationQuatFromBytes(unsigned char* buffer, glm::quat& quatOutput) {
|
||||
uint16_t quatParts[4];
|
||||
memcpy(&quatParts, buffer, sizeof(quatParts));
|
||||
|
||||
quatOutput.x = ((quatParts[0] / (float) std::numeric_limits<uint16_t>::max()) * 2.0) - 1.0;
|
||||
quatOutput.y = ((quatParts[1] / (float) std::numeric_limits<uint16_t>::max()) * 2.0) - 1.0;
|
||||
quatOutput.z = ((quatParts[2] / (float) std::numeric_limits<uint16_t>::max()) * 2.0) - 1.0;
|
||||
quatOutput.w = ((quatParts[3] / (float) std::numeric_limits<uint16_t>::max()) * 2.0) - 1.0;
|
||||
|
||||
return sizeof(quatParts);
|
||||
}
|
||||
|
||||
float SMALL_LIMIT = 10.0;
|
||||
float LARGE_LIMIT = 1000.0;
|
||||
|
||||
int packFloatRatioToTwoByte(unsigned char* buffer, float ratio) {
|
||||
// if the ratio is less than 10, then encode it as a positive number scaled from 0 to int16::max()
|
||||
int16_t ratioHolder;
|
||||
|
||||
if (ratio < SMALL_LIMIT) {
|
||||
const float SMALL_RATIO_CONVERSION_RATIO = (std::numeric_limits<int16_t>::max() / SMALL_LIMIT);
|
||||
ratioHolder = floorf(ratio * SMALL_RATIO_CONVERSION_RATIO);
|
||||
} else {
|
||||
const float LARGE_RATIO_CONVERSION_RATIO = std::numeric_limits<int16_t>::min() / LARGE_LIMIT;
|
||||
ratioHolder = floorf((std::min(ratio,LARGE_LIMIT) - SMALL_LIMIT) * LARGE_RATIO_CONVERSION_RATIO);
|
||||
}
|
||||
memcpy(buffer, &ratioHolder, sizeof(ratioHolder));
|
||||
return sizeof(ratioHolder);
|
||||
}
|
||||
|
||||
int unpackFloatRatioFromTwoByte(unsigned char* buffer, float& ratio) {
|
||||
int16_t ratioHolder;
|
||||
memcpy(&ratioHolder, buffer, sizeof(ratioHolder));
|
||||
|
||||
// If it's positive, than the original ratio was less than SMALL_LIMIT
|
||||
if (ratioHolder > 0) {
|
||||
ratio = (ratioHolder / (float) std::numeric_limits<int16_t>::max()) * SMALL_LIMIT;
|
||||
} else {
|
||||
// If it's negative, than the original ratio was between SMALL_LIMIT and LARGE_LIMIT
|
||||
ratio = ((ratioHolder / (float) std::numeric_limits<int16_t>::min()) * LARGE_LIMIT) + SMALL_LIMIT;
|
||||
}
|
||||
return sizeof(ratioHolder);
|
||||
}
|
||||
|
||||
int packClipValueToTwoByte(unsigned char* buffer, float clipValue) {
|
||||
// Clip values must be less than max signed 16bit integers
|
||||
assert(clipValue < std::numeric_limits<int16_t>::max());
|
||||
int16_t holder;
|
||||
|
||||
// if the clip is less than 10, then encode it as a positive number scaled from 0 to int16::max()
|
||||
if (clipValue < SMALL_LIMIT) {
|
||||
const float SMALL_RATIO_CONVERSION_RATIO = (std::numeric_limits<int16_t>::max() / SMALL_LIMIT);
|
||||
holder = floorf(clipValue * SMALL_RATIO_CONVERSION_RATIO);
|
||||
} else {
|
||||
// otherwise we store it as a negative integer
|
||||
holder = -1 * floorf(clipValue);
|
||||
}
|
||||
memcpy(buffer, &holder, sizeof(holder));
|
||||
return sizeof(holder);
|
||||
}
|
||||
|
||||
int unpackClipValueFromTwoByte(unsigned char* buffer, float& clipValue) {
|
||||
int16_t holder;
|
||||
memcpy(&holder, buffer, sizeof(holder));
|
||||
|
||||
// If it's positive, than the original clipValue was less than SMALL_LIMIT
|
||||
if (holder > 0) {
|
||||
clipValue = (holder / (float) std::numeric_limits<int16_t>::max()) * SMALL_LIMIT;
|
||||
} else {
|
||||
// If it's negative, than the original holder can be found as the opposite sign of holder
|
||||
clipValue = -1.0f * holder;
|
||||
}
|
||||
return sizeof(holder);
|
||||
}
|
||||
|
||||
int packFloatToByte(unsigned char* buffer, float value, float scaleBy) {
|
||||
unsigned char holder;
|
||||
const float CONVERSION_RATIO = (255 / scaleBy);
|
||||
holder = floorf(value * CONVERSION_RATIO);
|
||||
memcpy(buffer, &holder, sizeof(holder));
|
||||
return sizeof(holder);
|
||||
}
|
||||
|
||||
int unpackFloatFromByte(unsigned char* buffer, float& value, float scaleBy) {
|
||||
unsigned char holder;
|
||||
memcpy(&holder, buffer, sizeof(holder));
|
||||
value = ((float)holder / (float) 255) * scaleBy;
|
||||
return sizeof(holder);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,10 @@
|
|||
#define __hifi__AvatarData__
|
||||
|
||||
#include <string>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <AgentData.h>
|
||||
#include "HeadData.h"
|
||||
|
@ -19,15 +21,15 @@
|
|||
const int WANT_RESIN_AT_BIT = 0;
|
||||
const int WANT_COLOR_AT_BIT = 1;
|
||||
const int WANT_DELTA_AT_BIT = 2;
|
||||
const int KEY_STATE_START_BIT = 3; // 4th and 5th bits
|
||||
const int HAND_STATE_START_BIT = 5; // 6th and 7th bits
|
||||
|
||||
const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation
|
||||
|
||||
// this is where the coordinate system is represented
|
||||
const glm::vec3 AVATAR_RIGHT = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
const glm::vec3 AVATAR_UP = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
const glm::vec3 AVATAR_FRONT = glm::vec3(0.0f, 0.0f, -1.0f);
|
||||
|
||||
enum KeyState
|
||||
{
|
||||
NO_KEY_DOWN,
|
||||
NO_KEY_DOWN = 0,
|
||||
INSERT_KEY_DOWN,
|
||||
DELETE_KEY_DOWN
|
||||
};
|
||||
|
@ -59,23 +61,21 @@ public:
|
|||
|
||||
// getters for camera details
|
||||
const glm::vec3& getCameraPosition() const { return _cameraPosition; };
|
||||
const glm::vec3& getCameraDirection() const { return _cameraDirection; }
|
||||
const glm::vec3& getCameraUp() const { return _cameraUp; }
|
||||
const glm::vec3& getCameraRight() const { return _cameraRight; }
|
||||
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; }
|
||||
|
||||
glm::vec3 calculateCameraDirection() const;
|
||||
|
||||
// setters for camera details
|
||||
void setCameraPosition(const glm::vec3& position) { _cameraPosition = position; };
|
||||
void setCameraDirection(const glm::vec3& direction) { _cameraDirection = direction; }
|
||||
void setCameraUp(const glm::vec3& up) { _cameraUp = up; }
|
||||
void setCameraRight(const glm::vec3& right) { _cameraRight = right; }
|
||||
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 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; }
|
||||
|
||||
// key state
|
||||
void setKeyState(KeyState s) { _keyState = s; }
|
||||
|
@ -109,11 +109,7 @@ protected:
|
|||
|
||||
// camera details for the avatar
|
||||
glm::vec3 _cameraPosition;
|
||||
|
||||
// can we describe this in less space? For example, a Quaternion? or Euler angles?
|
||||
glm::vec3 _cameraDirection;
|
||||
glm::vec3 _cameraUp;
|
||||
glm::vec3 _cameraRight;
|
||||
glm::quat _cameraOrientation;
|
||||
float _cameraFov;
|
||||
float _cameraAspectRatio;
|
||||
float _cameraNearClip;
|
||||
|
@ -137,4 +133,31 @@ private:
|
|||
AvatarData& operator= (const AvatarData&);
|
||||
};
|
||||
|
||||
|
||||
// These pack/unpack functions are designed to start specific known types in as efficient a manner
|
||||
// as possible. Taking advantage of the known characteristics of the semantic types.
|
||||
|
||||
// Angles are known to be between 0 and 360deg, this allows us to encode in 16bits with great accuracy
|
||||
int packFloatAngleToTwoByte(unsigned char* buffer, float angle);
|
||||
int unpackFloatAngleFromTwoByte(uint16_t* byteAnglePointer, float* destinationPointer);
|
||||
|
||||
// Orientation Quats are known to have 4 normalized components be between -1.0 and 1.0
|
||||
// this allows us to encode each component in 16bits with great accuracy
|
||||
int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput);
|
||||
int unpackOrientationQuatFromBytes(unsigned char* buffer, glm::quat& quatOutput);
|
||||
|
||||
// Ratios need the be highly accurate when less than 10, but not very accurate above 10, and they
|
||||
// are never greater than 1000 to 1, this allows us to encode each component in 16bits
|
||||
int packFloatRatioToTwoByte(unsigned char* buffer, float ratio);
|
||||
int unpackFloatRatioFromTwoByte(unsigned char* buffer, float& ratio);
|
||||
|
||||
// Near/Far Clip values need the be highly accurate when less than 10, but only integer accuracy above 10 and
|
||||
// they are never greater than 16,000, this allows us to encode each component in 16bits
|
||||
int packClipValueToTwoByte(unsigned char* buffer, float clipValue);
|
||||
int unpackClipValueFromTwoByte(unsigned char* buffer, float& clipValue);
|
||||
|
||||
// Positive floats that don't need to be very precise
|
||||
int packFloatToByte(unsigned char* buffer, float value, float scaleBy);
|
||||
int unpackFloatFromByte(unsigned char* buffer, float& value, float scaleBy);
|
||||
|
||||
#endif /* defined(__hifi__AvatarData__) */
|
||||
|
|
|
@ -40,7 +40,7 @@ bool pingUnknownAgentThreadStopFlag = false;
|
|||
AgentList* AgentList::_sharedInstance = NULL;
|
||||
|
||||
AgentList* AgentList::createInstance(char ownerType, unsigned int socketListenPort) {
|
||||
if (_sharedInstance == NULL) {
|
||||
if (!_sharedInstance) {
|
||||
_sharedInstance = new AgentList(ownerType, socketListenPort);
|
||||
} else {
|
||||
printLog("AgentList createInstance called with existing instance.\n");
|
||||
|
@ -50,7 +50,7 @@ AgentList* AgentList::createInstance(char ownerType, unsigned int socketListenPo
|
|||
}
|
||||
|
||||
AgentList* AgentList::getInstance() {
|
||||
if (_sharedInstance == NULL) {
|
||||
if (!_sharedInstance) {
|
||||
printLog("AgentList getInstance called before call to createInstance. Returning NULL pointer.\n");
|
||||
}
|
||||
|
||||
|
@ -150,14 +150,12 @@ int AgentList::updateAgentWithData(sockaddr *senderAddress, unsigned char *packe
|
|||
int AgentList::updateAgentWithData(Agent *agent, unsigned char *packetData, int dataBytes) {
|
||||
agent->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
if (agent->getActiveSocket() != NULL) {
|
||||
if (agent->getActiveSocket()) {
|
||||
agent->recordBytesReceived(dataBytes);
|
||||
}
|
||||
|
||||
if (agent->getLinkedData() == NULL) {
|
||||
if (linkedDataCreateCallback != NULL) {
|
||||
linkedDataCreateCallback(agent);
|
||||
}
|
||||
if (!agent->getLinkedData() && linkedDataCreateCallback) {
|
||||
linkedDataCreateCallback(agent);
|
||||
}
|
||||
|
||||
return agent->getLinkedData()->parseData(packetData, dataBytes);
|
||||
|
@ -165,7 +163,7 @@ int AgentList::updateAgentWithData(Agent *agent, unsigned char *packetData, int
|
|||
|
||||
Agent* AgentList::agentWithAddress(sockaddr *senderAddress) {
|
||||
for(AgentList::iterator agent = begin(); agent != end(); agent++) {
|
||||
if (agent->getActiveSocket() != NULL && socketMatch(agent->getActiveSocket(), senderAddress)) {
|
||||
if (agent->getActiveSocket() && socketMatch(agent->getActiveSocket(), senderAddress)) {
|
||||
return &(*agent);
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +214,7 @@ int AgentList::processDomainServerList(unsigned char *packetData, size_t dataByt
|
|||
Agent* AgentList::addOrUpdateAgent(sockaddr* publicSocket, sockaddr* localSocket, char agentType, uint16_t agentId) {
|
||||
AgentList::iterator agent = end();
|
||||
|
||||
if (publicSocket != NULL) {
|
||||
if (publicSocket) {
|
||||
for (agent = begin(); agent != end(); agent++) {
|
||||
if (agent->matches(publicSocket, localSocket, agentType)) {
|
||||
// we already have this agent, stop checking
|
||||
|
@ -327,8 +325,7 @@ void *pingUnknownAgents(void *args) {
|
|||
for(AgentList::iterator agent = agentList->begin();
|
||||
agent != agentList->end();
|
||||
agent++) {
|
||||
if (agent->getActiveSocket() == NULL
|
||||
&& (agent->getPublicSocket() != NULL && agent->getLocalSocket() != NULL)) {
|
||||
if (!agent->getActiveSocket() && agent->getPublicSocket() && agent->getLocalSocket()) {
|
||||
// ping both of the sockets for the agent so we can figure out
|
||||
// which socket we can use
|
||||
agentList->getAgentSocket()->send(agent->getPublicSocket(), &PACKET_HEADER_PING, 1);
|
||||
|
|
|
@ -168,3 +168,88 @@ OctalCodeComparison compareOctalCodes(unsigned char* codeA, unsigned char* codeB
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
char getOctalCodeSectionValue(unsigned char* octalCode, int section) {
|
||||
int startAtByte = 1 + (BITS_IN_OCTAL * section / BITS_IN_BYTE);
|
||||
char startIndexInByte = (BITS_IN_OCTAL * section) % BITS_IN_BYTE;
|
||||
unsigned char* startByte = octalCode + startAtByte;
|
||||
|
||||
return sectionValue(startByte, startIndexInByte);
|
||||
}
|
||||
|
||||
void setOctalCodeSectionValue(unsigned char* octalCode, int section, char sectionValue) {
|
||||
int byteForSection = (BITS_IN_OCTAL * section / BITS_IN_BYTE);
|
||||
unsigned char* byteAt = octalCode + 1 + byteForSection;
|
||||
char bitInByte = (BITS_IN_OCTAL * section) % BITS_IN_BYTE;
|
||||
char shiftBy = BITS_IN_BYTE - bitInByte - BITS_IN_OCTAL;
|
||||
const unsigned char UNSHIFTED_MASK = 0x07;
|
||||
unsigned char shiftedMask;
|
||||
unsigned char shiftedValue;
|
||||
if (shiftBy >=0) {
|
||||
shiftedMask = UNSHIFTED_MASK << shiftBy;
|
||||
shiftedValue = sectionValue << shiftBy;
|
||||
} else {
|
||||
shiftedMask = UNSHIFTED_MASK >> -shiftBy;
|
||||
shiftedValue = sectionValue >> -shiftBy;
|
||||
}
|
||||
unsigned char oldValue = *byteAt & ~shiftedMask;
|
||||
unsigned char newValue = oldValue | shiftedValue;
|
||||
*byteAt = newValue;
|
||||
|
||||
// If the requested section is partially in the byte, then we
|
||||
// need to also set the portion of the section value in the next byte
|
||||
// there's only two cases where this happens, if the bit in byte is
|
||||
// 6, then it means that 1 extra bit lives in the next byte. If the
|
||||
// bit in this byte is 7 then 2 extra bits live in the next byte.
|
||||
const int FIRST_PARTIAL_BIT = 6;
|
||||
if (bitInByte >= FIRST_PARTIAL_BIT) {
|
||||
int bitsInFirstByte = BITS_IN_BYTE - bitInByte;
|
||||
int bitsInSecondByte = BITS_IN_OCTAL - bitsInFirstByte;
|
||||
shiftBy = BITS_IN_BYTE - bitsInSecondByte;
|
||||
|
||||
shiftedMask = UNSHIFTED_MASK << shiftBy;
|
||||
shiftedValue = sectionValue << shiftBy;
|
||||
|
||||
oldValue = byteAt[1] & ~shiftedMask;
|
||||
newValue = oldValue | shiftedValue;
|
||||
byteAt[1] = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char* chopOctalCode(unsigned char* originalOctalCode, int chopLevels) {
|
||||
int codeLength = numberOfThreeBitSectionsInCode(originalOctalCode);
|
||||
unsigned char* newCode = NULL;
|
||||
if (codeLength > chopLevels) {
|
||||
int newLength = codeLength - chopLevels;
|
||||
newCode = new unsigned char[newLength+1];
|
||||
*newCode = newLength; // set the length byte
|
||||
|
||||
for (int section = chopLevels; section < codeLength; section++) {
|
||||
char sectionValue = getOctalCodeSectionValue(originalOctalCode, section);
|
||||
setOctalCodeSectionValue(newCode, section - chopLevels, sectionValue);
|
||||
}
|
||||
}
|
||||
return newCode;
|
||||
}
|
||||
|
||||
unsigned char* rebaseOctalCode(unsigned char* originalOctalCode, unsigned char* newParentOctalCode, bool includeColorSpace) {
|
||||
int oldCodeLength = numberOfThreeBitSectionsInCode(originalOctalCode);
|
||||
int newParentCodeLength = numberOfThreeBitSectionsInCode(newParentOctalCode);
|
||||
int newCodeLength = newParentCodeLength + oldCodeLength;
|
||||
int bufferLength = newCodeLength + (includeColorSpace ? SIZE_OF_COLOR_DATA : 0);
|
||||
unsigned char* newCode = new unsigned char[bufferLength];
|
||||
*newCode = newCodeLength; // set the length byte
|
||||
|
||||
// copy parent code section first
|
||||
for (int sectionFromParent = 0; sectionFromParent < newParentCodeLength; sectionFromParent++) {
|
||||
char sectionValue = getOctalCodeSectionValue(newParentOctalCode, sectionFromParent);
|
||||
setOctalCodeSectionValue(newCode, sectionFromParent, sectionValue);
|
||||
}
|
||||
// copy original code section next
|
||||
for (int sectionFromOriginal = 0; sectionFromOriginal < oldCodeLength; sectionFromOriginal++) {
|
||||
char sectionValue = getOctalCodeSectionValue(originalOctalCode, sectionFromOriginal);
|
||||
setOctalCodeSectionValue(newCode, sectionFromOriginal + newParentCodeLength, sectionValue);
|
||||
}
|
||||
return newCode;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,12 +11,23 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
const int BITS_IN_BYTE = 8;
|
||||
const int BITS_IN_OCTAL = 3;
|
||||
const int NUMBER_OF_COLORS = 3; // RGB!
|
||||
const int SIZE_OF_COLOR_DATA = NUMBER_OF_COLORS * sizeof(unsigned char); // size in bytes
|
||||
const int RED_INDEX = 0;
|
||||
const int GREEN_INDEX = 1;
|
||||
const int BLUE_INDEX = 2;
|
||||
|
||||
void printOctalCode(unsigned char * octalCode);
|
||||
int bytesRequiredForCodeLength(unsigned char threeBitCodes);
|
||||
bool isDirectParentOfChild(unsigned char *parentOctalCode, unsigned char * childOctalCode);
|
||||
int branchIndexWithDescendant(unsigned char * ancestorOctalCode, unsigned char * descendantOctalCode);
|
||||
unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber);
|
||||
|
||||
int numberOfThreeBitSectionsInCode(unsigned char * octalCode);
|
||||
unsigned char* chopOctalCode(unsigned char* originalOctalCode, int chopLevels);
|
||||
unsigned char* rebaseOctalCode(unsigned char* originalOctalCode, unsigned char* newParentOctalCode,
|
||||
bool includeColorSpace = false);
|
||||
|
||||
// Note: copyFirstVertexForCode() is preferred because it doesn't allocate memory for the return
|
||||
// but other than that these do the same thing.
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
//
|
||||
// PacketHeaders.h
|
||||
// hifi
|
||||
|
|
|
@ -101,6 +101,15 @@ void setAtBit(unsigned char& byte, int bitIndex) {
|
|||
byte += (1 << (7 - bitIndex));
|
||||
}
|
||||
|
||||
int getSemiNibbleAt(unsigned char& byte, int bitIndex) {
|
||||
return (byte >> (7 - bitIndex) & 3); // semi-nibbles store 00, 01, 10, or 11
|
||||
}
|
||||
|
||||
void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value) {
|
||||
//assert(value <= 3 && value >= 0);
|
||||
byte += ((value & 3) << (7 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11
|
||||
}
|
||||
|
||||
|
||||
void switchToResourcesParentIfRequired() {
|
||||
#ifdef __APPLE__
|
||||
|
@ -421,3 +430,4 @@ int insertIntoSortedArrays(void* value, float key, int originalIndex,
|
|||
}
|
||||
return -1; // error case
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,10 @@ int numberOfOnes(unsigned char byte);
|
|||
bool oneAtBit(unsigned char byte, int bitIndex);
|
||||
void setAtBit(unsigned char& byte, int bitIndex);
|
||||
|
||||
int getSemiNibbleAt(unsigned char& byte, int bitIndex);
|
||||
void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value);
|
||||
|
||||
|
||||
void switchToResourcesParentIfRequired();
|
||||
|
||||
void loadRandomIdentifier(unsigned char* identifierBuffer, int numBytes);
|
||||
|
@ -71,7 +75,7 @@ struct VoxelDetail {
|
|||
unsigned char blue;
|
||||
};
|
||||
|
||||
unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, unsigned char g, unsigned char b );
|
||||
unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r = 0, unsigned char g = 0, unsigned char b = 0);
|
||||
bool createVoxelEditMessage(unsigned char command, short int sequence,
|
||||
int voxelCount, VoxelDetail* voxelDetails, unsigned char*& bufferOut, int& sizeOut);
|
||||
|
||||
|
@ -88,4 +92,5 @@ class debug {
|
|||
public:
|
||||
static const char* valueOf(bool checkValue) { return checkValue ? "yes" : "no"; };
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__SharedUtil__) */
|
||||
|
|
|
@ -292,13 +292,16 @@ glm::vec3 AABox::getClosestPointOnFace(const glm::vec4& origin, const glm::vec4&
|
|||
glm::vec4 diagonals[] = { secondAxisMinPlane + thirdAxisMaxPlane + offset,
|
||||
secondAxisMaxPlane + thirdAxisMaxPlane + offset };
|
||||
|
||||
float minDistance = FLT_MAX;
|
||||
for (int i = 0; i < sizeof(diagonals) / sizeof(diagonals[0]); i++) {
|
||||
float divisor = glm::dot(direction, diagonals[i]);
|
||||
if (fabs(divisor) < EPSILON) {
|
||||
continue; // segment is parallel to diagonal plane
|
||||
}
|
||||
float directionalDistance = -glm::dot(origin, diagonals[i]) / divisor;
|
||||
return getClosestPointOnFace(glm::vec3(origin + direction * directionalDistance), face);
|
||||
minDistance = glm::min(-glm::dot(origin, diagonals[i]) / divisor, minDistance);
|
||||
}
|
||||
if (minDistance != FLT_MAX) {
|
||||
return getClosestPointOnFace(glm::vec3(origin + direction * minDistance), face);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,28 +13,37 @@
|
|||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "ViewFrustum.h"
|
||||
#include "VoxelConstants.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "Log.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
ViewFrustum::ViewFrustum() :
|
||||
_position(glm::vec3(0,0,0)),
|
||||
_direction(glm::vec3(0,0,0)),
|
||||
_up(glm::vec3(0,0,0)),
|
||||
_right(glm::vec3(0,0,0)),
|
||||
_position(0,0,0),
|
||||
_orientation(),
|
||||
_direction(0,0,0),
|
||||
_up(0,0,0),
|
||||
_right(0,0,0),
|
||||
_fieldOfView(0.0),
|
||||
_aspectRatio(1.0),
|
||||
_nearClip(0.1),
|
||||
_farClip(500.0),
|
||||
_farTopLeft(glm::vec3(0,0,0)),
|
||||
_farTopRight(glm::vec3(0,0,0)),
|
||||
_farBottomLeft(glm::vec3(0,0,0)),
|
||||
_farBottomRight(glm::vec3(0,0,0)),
|
||||
_nearTopLeft(glm::vec3(0,0,0)),
|
||||
_nearTopRight(glm::vec3(0,0,0)),
|
||||
_nearBottomLeft(glm::vec3(0,0,0)),
|
||||
_nearBottomRight(glm::vec3(0,0,0)) { }
|
||||
_farTopLeft(0,0,0),
|
||||
_farTopRight(0,0,0),
|
||||
_farBottomLeft(0,0,0),
|
||||
_farBottomRight(0,0,0),
|
||||
_nearTopLeft(0,0,0),
|
||||
_nearTopRight(0,0,0),
|
||||
_nearBottomLeft(0,0,0),
|
||||
_nearBottomRight(0,0,0) { }
|
||||
|
||||
void ViewFrustum::setOrientation(const glm::quat& orientationAsQuaternion) {
|
||||
_orientation = orientationAsQuaternion;
|
||||
_right = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_RIGHT, 0.0f));
|
||||
_up = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_UP, 0.0f));
|
||||
_direction = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_FRONT, 0.0f));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// ViewFrustum::calculateViewFrustum()
|
||||
|
|
|
@ -21,6 +21,9 @@ private:
|
|||
|
||||
// camera location/orientation attributes
|
||||
glm::vec3 _position;
|
||||
glm::quat _orientation;
|
||||
|
||||
// calculated for orientation
|
||||
glm::vec3 _direction;
|
||||
glm::vec3 _up;
|
||||
glm::vec3 _right;
|
||||
|
@ -53,23 +56,23 @@ private:
|
|||
|
||||
public:
|
||||
// setters for camera attributes
|
||||
void setPosition (const glm::vec3& p) { _position = p; }
|
||||
void setOrientation (const glm::vec3& d, const glm::vec3& u, const glm::vec3& r )
|
||||
{ _direction = d; _up = u; _right = r; }
|
||||
void setPosition (const glm::vec3& p) { _position = p; };
|
||||
void setOrientation (const glm::quat& orientationAsQuaternion);
|
||||
|
||||
// getters for camera attributes
|
||||
const glm::vec3& getPosition() const { return _position; };
|
||||
const glm::vec3& getDirection() const { return _direction; };
|
||||
const glm::vec3& getUp() const { return _up; };
|
||||
const glm::vec3& getRight() const { return _right; };
|
||||
const glm::vec3& getPosition() const { return _position; };
|
||||
const glm::quat& getOrientation() const { return _orientation; };
|
||||
const glm::vec3& getDirection() const { return _direction; };
|
||||
const glm::vec3& getUp() const { return _up; };
|
||||
const glm::vec3& getRight() const { return _right; };
|
||||
|
||||
// setters for lens attributes
|
||||
void setFieldOfView ( float f ) { _fieldOfView = f; }
|
||||
void setAspectRatio ( float a ) { _aspectRatio = a; }
|
||||
void setNearClip ( float n ) { _nearClip = n; }
|
||||
void setFarClip ( float f ) { _farClip = f; }
|
||||
void setEyeOffsetPosition (const glm::vec3& p) { _eyeOffsetPosition = p; }
|
||||
void setEyeOffsetOrientation (const glm::quat& o) { _eyeOffsetOrientation = o; }
|
||||
void setFieldOfView ( float f ) { _fieldOfView = f; };
|
||||
void setAspectRatio ( float a ) { _aspectRatio = a; };
|
||||
void setNearClip ( float n ) { _nearClip = n; };
|
||||
void setFarClip ( float f ) { _farClip = f; };
|
||||
void setEyeOffsetPosition (const glm::vec3& p) { _eyeOffsetPosition = p; };
|
||||
void setEyeOffsetOrientation (const glm::quat& o) { _eyeOffsetOrientation = o; };
|
||||
|
||||
|
||||
// getters for lens attributes
|
||||
|
|
|
@ -13,6 +13,12 @@
|
|||
#define __hifi_VoxelConstants_h__
|
||||
|
||||
#include <limits.h>
|
||||
#include <OctalCode.h>
|
||||
|
||||
// this is where the coordinate system is represented
|
||||
const glm::vec3 IDENTITY_RIGHT = glm::vec3( 1.0f, 0.0f, 0.0f);
|
||||
const glm::vec3 IDENTITY_UP = glm::vec3( 0.0f, 1.0f, 0.0f);
|
||||
const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f);
|
||||
|
||||
const int TREE_SCALE = 128;
|
||||
|
||||
|
@ -23,11 +29,12 @@ const int MAX_VOXELS_PER_SYSTEM = 200000;
|
|||
const int VERTICES_PER_VOXEL = 24;
|
||||
const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL;
|
||||
const int INDICES_PER_VOXEL = 3 * 12;
|
||||
const int COLOR_VALUES_PER_VOXEL = 3 * VERTICES_PER_VOXEL;
|
||||
const int COLOR_VALUES_PER_VOXEL = NUMBER_OF_COLORS * VERTICES_PER_VOXEL;
|
||||
|
||||
typedef unsigned long int glBufferIndex;
|
||||
const glBufferIndex GLBUFFER_INDEX_UNKNOWN = ULONG_MAX;
|
||||
|
||||
const double SIXTY_FPS_IN_MILLISECONDS = 1000.0/60;
|
||||
const double VIEW_CULLING_RATE_IN_MILLISECONDS = 1000.0; // once a second is fine
|
||||
|
||||
#endif
|
||||
|
|
|
@ -53,7 +53,7 @@ VoxelTree::~VoxelTree() {
|
|||
// Recurses voxel tree calling the RecurseVoxelTreeOperation function for each node.
|
||||
// stops recursion if operation function returns false.
|
||||
void VoxelTree::recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData) {
|
||||
recurseNodeWithOperation(rootNode, operation,extraData);
|
||||
recurseNodeWithOperation(rootNode, operation, extraData);
|
||||
}
|
||||
|
||||
// Recurses voxel node with an operation function
|
||||
|
@ -212,10 +212,15 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData,
|
|||
}
|
||||
|
||||
void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int bufferSizeBytes,
|
||||
bool includeColor, bool includeExistsBits) {
|
||||
bool includeColor, bool includeExistsBits, VoxelNode* destinationNode) {
|
||||
int bytesRead = 0;
|
||||
unsigned char* bitstreamAt = bitstream;
|
||||
|
||||
// If destination node is not included, set it to root
|
||||
if (!destinationNode) {
|
||||
destinationNode = rootNode;
|
||||
}
|
||||
|
||||
_nodesChangedFromBitstream = 0;
|
||||
|
||||
// Keep looping through the buffer calling readNodeData() this allows us to pack multiple root-relative Octal codes
|
||||
|
@ -223,14 +228,14 @@ void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int
|
|||
// if there are more bytes after that, it's assumed to be another root relative tree
|
||||
|
||||
while (bitstreamAt < bitstream + bufferSizeBytes) {
|
||||
VoxelNode* bitstreamRootNode = nodeForOctalCode(rootNode, (unsigned char *)bitstreamAt, NULL);
|
||||
VoxelNode* bitstreamRootNode = nodeForOctalCode(destinationNode, (unsigned char *)bitstreamAt, NULL);
|
||||
if (*bitstreamAt != *bitstreamRootNode->getOctalCode()) {
|
||||
// if the octal code returned is not on the same level as
|
||||
// the code being searched for, we have VoxelNodes to create
|
||||
|
||||
// Note: we need to create this node relative to root, because we're assuming that the bitstream for the initial
|
||||
// octal code is always relative to root!
|
||||
bitstreamRootNode = createMissingNode(rootNode, (unsigned char*) bitstreamAt);
|
||||
bitstreamRootNode = createMissingNode(destinationNode, (unsigned char*) bitstreamAt);
|
||||
if (bitstreamRootNode->isDirty()) {
|
||||
_isDirty = true;
|
||||
_nodesChangedFromBitstream++;
|
||||
|
@ -281,9 +286,9 @@ void VoxelTree::deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool stage, b
|
|||
}
|
||||
}
|
||||
|
||||
// If we're not a colored leaf, and we have no children, then delete ourselves
|
||||
// This will collapse the empty tree above us.
|
||||
if (collapseEmptyTrees && parentNode->getChildCount() == 0 && !parentNode->isColored()) {
|
||||
// If we're in collapseEmptyTrees mode, and we're the last child of this parent, then delete the parent.
|
||||
// This will collapse the empty tree above us.
|
||||
if (collapseEmptyTrees && parentNode->getChildCount() == 0) {
|
||||
// Can't delete the root this way.
|
||||
if (parentNode != rootNode) {
|
||||
deleteVoxelCodeFromTree(parentNode->getOctalCode(), stage, collapseEmptyTrees);
|
||||
|
@ -862,7 +867,7 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe
|
|||
|
||||
int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned char* outputBuffer, int availableBytes,
|
||||
VoxelNodeBag& bag, const ViewFrustum* viewFrustum, bool includeColor, bool includeExistsBits,
|
||||
bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const {
|
||||
int chopLevels, bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const {
|
||||
|
||||
// How many bytes have we written so far at this level;
|
||||
int bytesWritten = 0;
|
||||
|
@ -873,16 +878,29 @@ int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned
|
|||
}
|
||||
|
||||
// write the octal code
|
||||
int codeLength = bytesRequiredForCodeLength(*node->getOctalCode());
|
||||
memcpy(outputBuffer,node->getOctalCode(),codeLength);
|
||||
|
||||
int codeLength;
|
||||
if (chopLevels) {
|
||||
unsigned char* newCode = chopOctalCode(node->getOctalCode(), chopLevels);
|
||||
if (newCode) {
|
||||
codeLength = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(newCode));
|
||||
memcpy(outputBuffer, newCode, codeLength);
|
||||
delete newCode;
|
||||
} else {
|
||||
codeLength = 1; // chopped to root!
|
||||
*outputBuffer = 0; // root
|
||||
}
|
||||
} else {
|
||||
codeLength = bytesRequiredForCodeLength(*node->getOctalCode());
|
||||
memcpy(outputBuffer, node->getOctalCode(), codeLength);
|
||||
}
|
||||
|
||||
outputBuffer += codeLength; // move the pointer
|
||||
bytesWritten += codeLength; // keep track of byte count
|
||||
availableBytes -= codeLength; // keep track or remaining space
|
||||
|
||||
int currentEncodeLevel = 0;
|
||||
int childBytesWritten = encodeTreeBitstreamRecursion(maxEncodeLevel, currentEncodeLevel, node, outputBuffer, availableBytes,
|
||||
bag, viewFrustum, includeColor, includeExistsBits,
|
||||
bag, viewFrustum, includeColor, includeExistsBits, chopLevels,
|
||||
deltaViewFrustum, lastViewFrustum);
|
||||
|
||||
// if childBytesWritten == 1 then something went wrong... that's not possible
|
||||
|
@ -907,7 +925,7 @@ int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned
|
|||
int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel, VoxelNode* node,
|
||||
unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
||||
const ViewFrustum* viewFrustum, bool includeColor, bool includeExistsBits,
|
||||
bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const {
|
||||
int chopLevels, bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const {
|
||||
|
||||
// How many bytes have we written so far at this level;
|
||||
int bytesAtThisLevel = 0;
|
||||
|
@ -1062,7 +1080,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
|
|||
int thisLevel = currentEncodeLevel;
|
||||
int childTreeBytesOut = encodeTreeBitstreamRecursion(maxEncodeLevel, thisLevel, childNode,
|
||||
outputBuffer, availableBytes, bag,
|
||||
viewFrustum, includeColor, includeExistsBits,
|
||||
viewFrustum, includeColor, includeExistsBits, chopLevels,
|
||||
deltaViewFrustum, lastViewFrustum);
|
||||
|
||||
// if the child wrote 0 bytes, it means that nothing below exists or was in view, or we ran out of space,
|
||||
|
@ -1105,7 +1123,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
|
|||
return bytesAtThisLevel;
|
||||
}
|
||||
|
||||
bool VoxelTree::readFromFileV2(const char* fileName) {
|
||||
bool VoxelTree::readFromSVOFile(const char* fileName) {
|
||||
std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate);
|
||||
if(file.is_open()) {
|
||||
printLog("loading file %s...\n", fileName);
|
||||
|
@ -1126,7 +1144,7 @@ bool VoxelTree::readFromFileV2(const char* fileName) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void VoxelTree::writeToFileV2(const char* fileName) const {
|
||||
void VoxelTree::writeToSVOFile(const char* fileName, VoxelNode* node) const {
|
||||
|
||||
std::ofstream file(fileName, std::ios::out|std::ios::binary);
|
||||
|
||||
|
@ -1134,7 +1152,12 @@ void VoxelTree::writeToFileV2(const char* fileName) const {
|
|||
printLog("saving to file %s...\n", fileName);
|
||||
|
||||
VoxelNodeBag nodeBag;
|
||||
nodeBag.insert(rootNode);
|
||||
// If we were given a specific node, start from there, otherwise start from root
|
||||
if (node) {
|
||||
nodeBag.insert(node);
|
||||
} else {
|
||||
nodeBag.insert(rootNode);
|
||||
}
|
||||
|
||||
static unsigned char outputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static
|
||||
int bytesWritten = 0;
|
||||
|
@ -1160,3 +1183,47 @@ bool VoxelTree::countVoxelsOperation(VoxelNode* node, void* extraData) {
|
|||
(*(unsigned long*)extraData)++;
|
||||
return true; // keep going
|
||||
}
|
||||
|
||||
void VoxelTree::copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinationTree, bool rebaseToRoot) {
|
||||
VoxelNodeBag nodeBag;
|
||||
nodeBag.insert(startNode);
|
||||
int chopLevels = 0;
|
||||
if (rebaseToRoot) {
|
||||
chopLevels = numberOfThreeBitSectionsInCode(startNode->getOctalCode());
|
||||
}
|
||||
|
||||
static unsigned char outputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static
|
||||
int bytesWritten = 0;
|
||||
|
||||
while (!nodeBag.isEmpty()) {
|
||||
VoxelNode* subTree = nodeBag.extract();
|
||||
|
||||
// ask our tree to write a bitsteam
|
||||
bytesWritten = encodeTreeBitstream(INT_MAX, subTree, &outputBuffer[0],
|
||||
MAX_VOXEL_PACKET_SIZE - 1, nodeBag, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS, chopLevels);
|
||||
|
||||
// ask destination tree to read the bitstream
|
||||
destinationTree->readBitstreamToTree(&outputBuffer[0], bytesWritten, WANT_COLOR, NO_EXISTS_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelTree::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode) {
|
||||
VoxelNodeBag nodeBag;
|
||||
// If we were given a specific node, start from there, otherwise start from root
|
||||
nodeBag.insert(sourceTree->rootNode);
|
||||
|
||||
static unsigned char outputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static
|
||||
int bytesWritten = 0;
|
||||
|
||||
while (!nodeBag.isEmpty()) {
|
||||
VoxelNode* subTree = nodeBag.extract();
|
||||
|
||||
// ask our tree to write a bitsteam
|
||||
bytesWritten = sourceTree->encodeTreeBitstream(INT_MAX, subTree, &outputBuffer[0],
|
||||
MAX_VOXEL_PACKET_SIZE - 1, nodeBag, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS);
|
||||
|
||||
// ask destination tree to read the bitstream
|
||||
readBitstreamToTree(&outputBuffer[0], bytesWritten, WANT_COLOR, NO_EXISTS_BITS, destinationNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,19 +44,20 @@ public:
|
|||
VoxelTree(bool shouldReaverage = false);
|
||||
~VoxelTree();
|
||||
|
||||
VoxelNode *rootNode;
|
||||
VoxelNode* rootNode;
|
||||
int leavesWrittenToBitstream;
|
||||
|
||||
void eraseAllVoxels();
|
||||
|
||||
void processRemoveVoxelBitstream(unsigned char * bitstream, int bufferSizeBytes);
|
||||
void readBitstreamToTree(unsigned char * bitstream, unsigned long int bufferSizeBytes,
|
||||
bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS);
|
||||
void readCodeColorBufferToTree(unsigned char *codeColorBuffer, bool destructive = false);
|
||||
void deleteVoxelCodeFromTree(unsigned char *codeBuffer, bool stage = ACTUALLY_DELETE,
|
||||
void processRemoveVoxelBitstream(unsigned char* bitstream, int bufferSizeBytes);
|
||||
void readBitstreamToTree(unsigned char* bitstream, unsigned long int bufferSizeBytes,
|
||||
bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS,
|
||||
VoxelNode* destinationNode = NULL);
|
||||
void readCodeColorBufferToTree(unsigned char* codeColorBuffer, bool destructive = false);
|
||||
void deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool stage = ACTUALLY_DELETE,
|
||||
bool collapseEmptyTrees = DONT_COLLAPSE);
|
||||
void printTreeForDebugging(VoxelNode *startNode);
|
||||
void reaverageVoxelColors(VoxelNode *startNode);
|
||||
void printTreeForDebugging(VoxelNode* startNode);
|
||||
void reaverageVoxelColors(VoxelNode* startNode);
|
||||
|
||||
void deleteVoxelAt(float x, float y, float z, float s, bool stage = false);
|
||||
VoxelNode* getVoxelAt(float x, float y, float z, float s) const;
|
||||
|
@ -70,7 +71,7 @@ public:
|
|||
|
||||
int encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned char* outputBuffer, int availableBytes,
|
||||
VoxelNodeBag& bag, const ViewFrustum* viewFrustum,
|
||||
bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS,
|
||||
bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS, int chopLevels = 0,
|
||||
bool deltaViewFrustum = false, const ViewFrustum* lastViewFrustum = NULL) const;
|
||||
|
||||
int searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag,
|
||||
|
@ -91,16 +92,19 @@ public:
|
|||
void loadVoxelsFile(const char* fileName, bool wantColorRandomizer);
|
||||
|
||||
// these will read/write files that match the wireformat, excluding the 'V' leading
|
||||
void writeToFileV2(const char* filename) const;
|
||||
bool readFromFileV2(const char* filename);
|
||||
void writeToSVOFile(const char* filename, VoxelNode* node = NULL) const;
|
||||
bool readFromSVOFile(const char* filename);
|
||||
|
||||
unsigned long getVoxelCount();
|
||||
|
||||
void copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinationTree, bool rebaseToRoot);
|
||||
void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode);
|
||||
|
||||
private:
|
||||
int encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel,
|
||||
VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
||||
const ViewFrustum* viewFrustum, bool includeColor, bool includeExistsBits,
|
||||
bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const;
|
||||
const ViewFrustum* viewFrustum, bool includeColor, bool includeExistsBits,
|
||||
int chopLevels, bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const;
|
||||
|
||||
int searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel,
|
||||
VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag,
|
||||
|
|
|
@ -92,7 +92,7 @@ int main(int argc, const char * argv[])
|
|||
unsigned long nodeCount = myTree.getVoxelCount();
|
||||
printf("Nodes after adding scenes: %ld nodes\n", nodeCount);
|
||||
|
||||
myTree.writeToFileV2("voxels.hio2");
|
||||
myTree.writeToSVOFile("voxels.svo");
|
||||
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -48,7 +48,7 @@ bool VoxelAgentData::updateCurrentViewFrustum() {
|
|||
ViewFrustum newestViewFrustum;
|
||||
// get position and orientation details from the camera
|
||||
newestViewFrustum.setPosition(getCameraPosition());
|
||||
newestViewFrustum.setOrientation(getCameraDirection(), getCameraUp(), getCameraRight());
|
||||
newestViewFrustum.setOrientation(getCameraOrientation());
|
||||
|
||||
// Also make sure it's got the correct lens details from the camera
|
||||
newestViewFrustum.setFieldOfView(getCameraFov());
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
#include <ifaddrs.h>
|
||||
#endif
|
||||
|
||||
const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.hio2";
|
||||
const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.hio2";
|
||||
const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo";
|
||||
const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo";
|
||||
const double VOXEL_PERSIST_INTERVAL = 1000.0 * 30; // every 30 seconds
|
||||
|
||||
const int VOXEL_LISTEN_PORT = 40106;
|
||||
|
@ -399,10 +399,10 @@ void persistVoxelsWhenDirty() {
|
|||
|
||||
{
|
||||
PerformanceWarning warn(::shouldShowAnimationDebug,
|
||||
"persistVoxelsWhenDirty() - writeToFileV2()", ::shouldShowAnimationDebug);
|
||||
"persistVoxelsWhenDirty() - writeToSVOFile()", ::shouldShowAnimationDebug);
|
||||
|
||||
printf("saving voxels to file...\n");
|
||||
randomTree.writeToFileV2(::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE);
|
||||
randomTree.writeToSVOFile(::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE);
|
||||
randomTree.clearDirtyBit(); // tree is clean after saving
|
||||
printf("DONE saving voxels to file...\n");
|
||||
}
|
||||
|
@ -505,7 +505,7 @@ int main(int argc, const char * argv[]) {
|
|||
bool persistantFileRead = false;
|
||||
if (::wantVoxelPersist) {
|
||||
printf("loading voxels from file...\n");
|
||||
persistantFileRead = ::randomTree.readFromFileV2(::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE);
|
||||
persistantFileRead = ::randomTree.readFromSVOFile(::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE);
|
||||
::randomTree.clearDirtyBit(); // the tree is clean since we just loaded it
|
||||
printf("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead));
|
||||
unsigned long nodeCount = ::randomTree.getVoxelCount();
|
||||
|
@ -517,7 +517,7 @@ int main(int argc, const char * argv[]) {
|
|||
const char* INPUT_FILE = "-i";
|
||||
const char* voxelsFilename = getCmdOption(argc, argv, INPUT_FILE);
|
||||
if (voxelsFilename) {
|
||||
randomTree.loadVoxelsFile(voxelsFilename,wantColorRandomizer);
|
||||
randomTree.readFromSVOFile(voxelsFilename);
|
||||
}
|
||||
|
||||
// Check to see if the user passed in a command line option for setting packet send rate
|
||||
|
|
Loading…
Reference in a new issue