Merge 19358 with upstream Master

This commit is contained in:
atlante45 2013-06-05 01:43:47 +02:00
commit d539ea283e
30 changed files with 1874 additions and 703 deletions

View file

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

View file

@ -32,17 +32,75 @@
// 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 v3Direction;
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 = gl_Color + fMiePhase * gl_SecondaryColor;
gl_FragColor.rgb = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb;
gl_FragColor.a = gl_FragColor.b;
}

View file

@ -1,100 +1,65 @@
#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 vertex shader
//
// Author: Sean O'Neil
//
// Copyright (c) 2004 Sean O'Neil
//
uniform vec3 v3CameraPos; // The camera's current position
uniform vec3 v3LightPos; // The direction vector to the light source
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;
varying vec3 v3Direction;
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 = gl_Vertex.xyz;
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;
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
gl_FrontSecondaryColor.rgb = v3FrontColor * fKmESun;
gl_FrontColor.rgb = v3FrontColor * (v3InvWavelength * fKrESun);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
v3Direction = v3CameraPos - v3Pos;
}
#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 vertex shader
//
// Author: Sean O'Neil
//
// Copyright (c) 2004 Sean O'Neil
//
uniform vec3 v3CameraPos; // The camera's current position
uniform vec3 v3LightPos; // The direction vector to the light source
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;
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)
position = gl_Vertex.xyz;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

View file

@ -1,48 +1,113 @@
#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 v3LightPos;
uniform float g;
uniform float g2;
varying vec3 v3Direction;
void main (void)
{
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 = gl_Color + fMiePhase * gl_SecondaryColor;
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 v3LightPos; // The direction vector to the light source
uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels
uniform float fCameraHeight2; // fCameraHeight^2
uniform float fOuterRadius; // The outer (atmosphere) radius
uniform float fOuterRadius2; // fOuterRadius^2
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
uniform float g;
uniform float g2;
const int nSamples = 2;
const float fSamples = 2.0;
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 closest intersection of the ray with the outer atmosphere (which is the near point of the ray passing through the atmosphere)
float B = 2.0 * dot(v3CameraPos, v3Ray);
float C = fCameraHeight2 - fOuterRadius2;
float fDet = max(0.0, B*B - 4.0 * C);
float fNear = 0.5 * (-B - sqrt(fDet));
// Calculate the ray's starting position, then calculate its scattering offset
vec3 v3Start = v3CameraPos + v3Ray * fNear;
fFar -= fNear;
float fStartAngle = dot(v3Ray, v3Start) / fOuterRadius;
float fStartDepth = exp(-1.0 / fScaleDepth);
float fStartOffset = fStartDepth * 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;
}
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);
vec3 color = v3FrontColor * (v3InvWavelength * fKrESun);
vec3 secondaryColor = v3FrontColor * fKmESun;
gl_FragColor.rgb = color + fMiePhase * secondaryColor;
gl_FragColor.a = gl_FragColor.b;
}

View file

@ -1,109 +1,41 @@
#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 vertex shader
//
// Author: Sean O'Neil
//
// Copyright (c) 2004 Sean O'Neil
//
uniform vec3 v3CameraPos; // The camera's current position
uniform vec3 v3LightPos; // The direction vector to the light source
uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels
uniform float fCameraHeight2; // fCameraHeight^2
uniform float fOuterRadius; // The outer (atmosphere) radius
uniform float fOuterRadius2; // fOuterRadius^2
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;
varying vec3 v3Direction;
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 = gl_Vertex.xyz;
vec3 v3Ray = v3Pos - v3CameraPos;
float fFar = length(v3Ray);
v3Ray /= fFar;
// Calculate the closest intersection of the ray with the outer atmosphere (which is the near point of the ray passing through the atmosphere)
float B = 2.0 * dot(v3CameraPos, v3Ray);
float C = fCameraHeight2 - fOuterRadius2;
float fDet = max(0.0, B*B - 4.0 * C);
float fNear = 0.5 * (-B - sqrt(fDet));
// Calculate the ray's starting position, then calculate its scattering offset
vec3 v3Start = v3CameraPos + v3Ray * fNear;
fFar -= fNear;
float fStartAngle = dot(v3Ray, v3Start) / fOuterRadius;
float fStartDepth = exp(-1.0 / fScaleDepth);
float fStartOffset = fStartDepth * 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;
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
gl_FrontSecondaryColor.rgb = v3FrontColor * fKmESun;
gl_FrontColor.rgb = v3FrontColor * (v3InvWavelength * fKrESun);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
v3Direction = v3CameraPos - v3Pos;
}
#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 vertex shader
//
// Author: Sean O'Neil
//
// Copyright (c) 2004 Sean O'Neil
//
varying vec3 position;
void main(void)
{
position = gl_Vertex.xyz;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

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

View file

@ -21,16 +21,23 @@
#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>
@ -41,6 +48,7 @@
#include <PerfStat.h>
#include <AudioInjectionManager.h>
#include <AudioInjector.h>
#include <OctalCode.h>
#include "Application.h"
#include "InterfaceConfig.h"
@ -52,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";
@ -159,8 +169,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");
@ -290,22 +298,22 @@ void Application::paintGL() {
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
_myCamera.setTightness (100.0f);
_myCamera.setTargetPosition(_myAvatar.getSpringyHeadPosition());
_myCamera.setTargetPosition(_myAvatar.getBallPosition(AVATAR_JOINT_HEAD_BASE));
_myCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f)));
} else if (OculusManager::isConnected()) {
_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());
}
@ -615,6 +623,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();
@ -832,7 +846,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);
@ -846,7 +860,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();
@ -910,24 +927,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);
}
}
}
}
@ -959,6 +981,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();
}
@ -1115,6 +1163,172 @@ void Application::chooseVoxelPaintColor() {
_window->activateWindow();
}
<<<<<<< HEAD
=======
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;
}
}
>>>>>>> 82c1ee2062577f614cfde096f08adfc9e83e4f0f
void Application::initMenu() {
QMenuBar* menuBar = new QMenuBar();
_window->setMenuBar(menuBar);
@ -1122,6 +1336,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()));
@ -1162,9 +1379,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);
@ -1173,26 +1388,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);
@ -1218,6 +1447,7 @@ 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);
<<<<<<< HEAD
QMenu* settingsMenu = menuBar->addMenu("Settings");
(_settingsAutosave = settingsMenu->addAction("Autosave", this, SLOT(setAutosave(bool))))->setCheckable(true);
@ -1226,6 +1456,11 @@ void Application::initMenu() {
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);
>>>>>>> 82c1ee2062577f614cfde096f08adfc9e83e4f0f
}
void Application::updateFrustumRenderModeAction() {
@ -1260,8 +1495,6 @@ void Application::initDisplay() {
void Application::init() {
_voxels.init();
_voxels.setViewerAvatar(&_myAvatar);
_voxels.setCamera(&_myCamera);
_environment.init();
@ -1272,11 +1505,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();
@ -1392,7 +1628,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();
@ -1404,17 +1640,6 @@ void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
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);
@ -1575,7 +1800,7 @@ void Application::displayOculus(Camera& whichCamera) {
glPopMatrix();
}
void Application::displaySide(Camera& whichCamera) {
// transform by eye offset
@ -2015,7 +2240,6 @@ void Application::maybeEditVoxelUnderCursor() {
//_myAvatar.getPosition()
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++) {
@ -2066,6 +2290,8 @@ void Application::maybeEditVoxelUnderCursor() {
}
} else if (_deleteVoxelMode->isChecked()) {
deleteVoxelUnderCursor();
} else if (_eyedropperMode->isChecked()) {
eyedropperVoxelUnderCursor();
}
}
@ -2081,7 +2307,7 @@ void Application::deleteVoxelUnderCursor() {
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);
@ -2090,6 +2316,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);
}
@ -2143,7 +2383,9 @@ QAction* Application::checkedVoxelModeAction() const {
void Application::attachNewHeadToAgent(Agent* newAgent) {
if (newAgent->getLinkedData() == NULL) {
newAgent->setLinkedData(new Avatar(newAgent));
Avatar* newAvatar = new Avatar(newAgent);
newAvatar->init();
newAgent->setLinkedData(newAvatar);
}
}

View file

@ -39,6 +39,8 @@ class QGLWidget;
class QKeyEvent;
class QMainWindow;
class QMouseEvent;
class QNetworkAccessManager;
class QSettings;
class QWheelEvent;
class Agent;
@ -67,10 +69,67 @@ 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(); }
<<<<<<< HEAD
=======
QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
/*!
@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;
/*!
@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);
>>>>>>> 82c1ee2062577f614cfde096f08adfc9e83e4f0f
private slots:
@ -78,6 +137,8 @@ private slots:
void idle();
void terminate();
void editPreferences();
void pair();
void setHead(bool head);
@ -106,6 +167,7 @@ private slots:
void decreaseVoxelSize();
void increaseVoxelSize();
void chooseVoxelPaintColor();
<<<<<<< HEAD
void setAutosave(bool wantsAutosave);
void loadSettings(QSettings* set = NULL);
@ -113,7 +175,17 @@ private slots:
void importSettings();
void exportSettings();
=======
void exportVoxels();
void importVoxels();
void cutVoxels();
void copyVoxels();
void pasteVoxels();
>>>>>>> 82c1ee2062577f614cfde096f08adfc9e83e4f0f
private:
static bool sendVoxelsOperation(VoxelNode* node, void* extraData);
void initMenu();
void updateFrustumRenderModeAction();
@ -134,7 +206,7 @@ private:
void shiftPaintingColor();
void maybeEditVoxelUnderCursor();
void deleteVoxelUnderCursor();
void eyedropperVoxelUnderCursor();
void goHome();
void resetSensors();
@ -176,6 +248,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
@ -185,6 +259,9 @@ private:
QAction* _frustumRenderModeAction;
QAction* _settingsAutosave; // Whether settings are saved automatically
QNetworkAccessManager* _networkAccessManager;
QSettings* _settings;
SerialInterface _serialPort;
bool _displayLevels;
@ -201,6 +278,8 @@ private:
Stars _stars;
VoxelSystem _voxels;
VoxelTree _clipboardTree; // if I copy/paste
QByteArray _voxelsFilename;
bool _wantToKillLocalVoxels;

View file

@ -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;
@ -143,7 +143,7 @@ int audioCallback (const void* inputBuffer,
unsigned char *currentPacketPtr = dataPacket + 1;
// memcpy the three float positions
memcpy(currentPacketPtr, &interfaceAvatar->getHeadPosition(), sizeof(float) * 3);
memcpy(currentPacketPtr, &interfaceAvatar->getHeadJointPosition(), sizeof(float) * 3);
currentPacketPtr += (sizeof(float) * 3);
// tell the mixer not to add additional attenuation to our source

View file

@ -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;
@ -64,7 +63,8 @@ float chatMessageHeight = 0.20;
Avatar::Avatar(Agent* owningAgent) :
AvatarData(owningAgent),
_head(this),
_TEST_bigSphereRadius(0.4f),
_ballSpringsInitialized(false),
_TEST_bigSphereRadius(0.5f),
_TEST_bigSpherePosition(5.0f, _TEST_bigSphereRadius, 5.0f),
_mousePressed(false),
_bodyPitchDelta(0.0f),
@ -87,7 +87,8 @@ Avatar::Avatar(Agent* owningAgent) :
_mouseRayDirection(0.0f, 0.0f, 0.0f),
_interactingOther(NULL),
_cumulativeMouseYaw(0.0f),
_isMouseTurningRight(false)
_isMouseTurningRight(false),
_voxels(this)
{
// give the pointer to our head to inherited _headData variable from AvatarData
@ -101,10 +102,10 @@ Avatar::Avatar(Agent* owningAgent) :
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;
_pelvisStandingHeight = _skeleton.getPelvisStandingHeight() + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius;
_pelvisFloatingHeight = _skeleton.getPelvisFloatingHeight() + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius;
_avatarTouch.setReachableRadius(PERIPERSONAL_RADIUS);
@ -118,49 +119,142 @@ Avatar::Avatar(Agent* owningAgent) :
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].parentOffset = glm::vec3(-0.1, -0.1, 0.0);
// specify the parent BALL for each ball
_bodyBall[ BODY_BALL_PELVIS ].parentBall = BODY_BALL_NULL;
_bodyBall[ BODY_BALL_TORSO ].parentBall = BODY_BALL_PELVIS;
_bodyBall[ BODY_BALL_CHEST ].parentBall = BODY_BALL_TORSO;
_bodyBall[ BODY_BALL_NECK_BASE ].parentBall = BODY_BALL_CHEST;
_bodyBall[ BODY_BALL_HEAD_BASE ].parentBall = BODY_BALL_NECK_BASE;
_bodyBall[ BODY_BALL_HEAD_TOP ].parentBall = BODY_BALL_HEAD_BASE;
_bodyBall[ BODY_BALL_LEFT_COLLAR ].parentBall = BODY_BALL_CHEST;
_bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentBall = BODY_BALL_LEFT_COLLAR;
_bodyBall[ BODY_BALL_LEFT_ELBOW ].parentBall = BODY_BALL_LEFT_SHOULDER;
_bodyBall[ BODY_BALL_LEFT_WRIST ].parentBall = BODY_BALL_LEFT_ELBOW;
_bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentBall = BODY_BALL_LEFT_WRIST;
_bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentBall = BODY_BALL_CHEST;
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentBall = BODY_BALL_RIGHT_COLLAR;
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentBall = BODY_BALL_RIGHT_SHOULDER;
_bodyBall[ BODY_BALL_RIGHT_WRIST ].parentBall = BODY_BALL_RIGHT_ELBOW;
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentBall = BODY_BALL_RIGHT_WRIST;
_bodyBall[ BODY_BALL_LEFT_HIP ].parentBall = BODY_BALL_PELVIS;
//_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;
_bodyBall[ AVATAR_JOINT_RIGHT_WRIST ].isCollidable = false;
_bodyBall[ AVATAR_JOINT_RIGHT_FINGERTIPS].isCollidable = false;
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].isCollidable = false;
_bodyBall[ BODY_BALL_RIGHT_WRIST ].isCollidable = false;
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS].isCollidable = false;
*/
}
@ -170,6 +264,10 @@ Avatar::~Avatar() {
delete _balls;
}
void Avatar::init() {
_voxels.init();
}
void Avatar::reset() {
_head.reset();
}
@ -275,6 +373,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;
@ -456,22 +573,22 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
right * _head.getLeanSideways() +
front * _head.getLeanForward();
_bodyBall[ AVATAR_JOINT_TORSO ].position += headLean * 0.1f;
_bodyBall[ AVATAR_JOINT_CHEST ].position += headLean * 0.4f;
_bodyBall[ AVATAR_JOINT_NECK_BASE ].position += headLean * 0.7f;
_bodyBall[ AVATAR_JOINT_HEAD_BASE ].position += headLean * 1.0f;
_bodyBall[ BODY_BALL_TORSO ].position += headLean * 0.1f;
_bodyBall[ BODY_BALL_CHEST ].position += headLean * 0.4f;
_bodyBall[ BODY_BALL_NECK_BASE ].position += headLean * 0.7f;
_bodyBall[ BODY_BALL_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 +602,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);
@ -500,12 +617,12 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
void Avatar::checkForMouseRayTouching() {
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) {
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
glm::vec3 directionToBodySphere = glm::normalize(_bodyBall[b].position - _mouseRayOrigin);
float dot = glm::dot(directionToBodySphere, _mouseRayDirection);
float range = _bodyBall[b].radius * JOINT_TOUCH_RANGE;
float range = _bodyBall[b].radius * MOUSE_RAY_TOUCH_RANGE;
if (dot > (1.0f - range)) {
_bodyBall[b].touchForce = (dot - (1.0f - range)) / range;
@ -557,11 +674,6 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
Avatar *otherAvatar = (Avatar *)agent->getLinkedData();
//Test: Show angle between your fwd vector and nearest avatar
//glm::vec3 vectorBetweenUs = otherAvatar->getJointPosition(AVATAR_JOINT_PELVIS) -
// getJointPosition(AVATAR_JOINT_PELVIS);
//printLog("Angle between: %f\n", angleBetween(vectorBetweenUs, getBodyFrontDirection()));
// test whether shoulders are close enough to allow for reaching to touch hands
glm::vec3 v(_position - otherAvatar->_position);
float distance = glm::length(v);
@ -579,7 +691,7 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
_avatarTouch.setHasInteractingOther(true);
_avatarTouch.setYourBodyPosition(_interactingOther->_position);
_avatarTouch.setYourHandPosition(_interactingOther->_bodyBall[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position);
_avatarTouch.setYourHandPosition(_interactingOther->_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].position);
_avatarTouch.setYourOrientation (_interactingOther->getOrientation());
_avatarTouch.setYourHandState (_interactingOther->_handState);
@ -650,32 +762,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;
}
}
@ -729,7 +838,7 @@ void Avatar::updateAvatarCollisions(float deltaTime) {
// Reset detector for nearest avatar
_distanceToNearestAvatar = std::numeric_limits<float>::max();
//loop through all the other avatars for potential interactions...
// loop through all the other avatars for potential interactions...
AgentList* agentList = AgentList::getInstance();
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
@ -739,7 +848,7 @@ void Avatar::updateAvatarCollisions(float deltaTime) {
glm::vec3 vectorBetweenBoundingSpheres(_position - otherAvatar->_position);
if (glm::length(vectorBetweenBoundingSpheres) < _height * ONE_HALF + otherAvatar->_height * ONE_HALF) {
//apply forces from collision
// apply forces from collision
applyCollisionWithOtherAvatar(otherAvatar, deltaTime);
}
@ -755,30 +864,30 @@ 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);
// loop through the joints of each avatar to check for every possible collision
for (int b=1; b<NUM_AVATAR_JOINTS; b++) {
// loop through the body balls of each avatar to check for every possible collision
for (int b = 1; b < NUM_AVATAR_BODY_BALLS; b++) {
if (_bodyBall[b].isCollidable) {
for (int o=b+1; o<NUM_AVATAR_JOINTS; o++) {
for (int o = b+1; o < NUM_AVATAR_BODY_BALLS; o++) {
if (otherAvatar->_bodyBall[o].isCollidable) {
glm::vec3 vectorBetweenJoints(_bodyBall[b].position - otherAvatar->_bodyBall[o].position);
float distanceBetweenJoints = glm::length(vectorBetweenJoints);
glm::vec3 vectorBetweenBalls(_bodyBall[b].position - otherAvatar->_bodyBall[o].position);
float distanceBetweenBalls = glm::length(vectorBetweenBalls);
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
if (distanceBetweenJoints < combinedRadius * COLLISION_RADIUS_SCALAR) {
glm::vec3 directionVector = vectorBetweenJoints / distanceBetweenJoints;
if (distanceBetweenBalls < combinedRadius * COLLISION_RADIUS_SCALAR) {
glm::vec3 directionVector = vectorBetweenBalls / distanceBetweenBalls;
// 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;
@ -793,7 +902,7 @@ void Avatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTime
} // b loop
} // collidable
//apply force on the whole body
// apply force on the whole body
_velocity += bodyPushForce;
}
@ -833,7 +942,7 @@ void Avatar::render(bool lookingInMirror) {
// render a simple round on the ground projected down from the avatar's position
renderDiskShadow(_position, glm::vec3(0.0f, 1.0f, 0.0f), 0.1f, 0.2f);
//render body
// render body
renderBody(lookingInMirror);
// if this is my avatar, then render my interactions with the other avatar
@ -857,7 +966,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,62 +1002,86 @@ 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++) {
glm::vec3 springVector(_bodyBall[b].position);
if (_skeleton.joint[b].parent == AVATAR_JOINT_NULL) {
springVector -= _position;
}
else {
springVector -= _bodyBall[ _skeleton.joint[b].parent ].position;
}
float 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 (_skeleton.joint[b].parent != AVATAR_JOINT_NULL) {
_bodyBall[_skeleton.joint[b].parent].velocity += springDirection * force;
glm::quat orientation = getOrientation();
glm::vec3 jointDirection = orientation * JOINT_DIRECTION;
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
glm::vec3 springVector;
float length = 0.0f;
if (_ballSpringsInitialized) {
// apply spring forces
springVector = _bodyBall[b].position;
if (b == BODY_BALL_PELVIS) {
springVector -= _position;
} else {
springVector -= _bodyBall[_bodyBall[b].parentBall].position;
}
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);
}
/*
//apply forces from touch...
if (_skeleton.joint[b].touchForce > 0.0) {
_skeleton.joint[b].springyVelocity += _mouseRayDirection * _skeleton.joint[b].touchForce * 0.7f;
// apply forces from touch...
if (_bodyBall[b].touchForce > 0.0) {
_bodyBall[b].velocity += _mouseRayDirection * _bodyBall[b].touchForce * 0.7f;
}
*/
//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;
}
}
}
@ -1009,9 +1142,12 @@ void Avatar::renderBody(bool lookingInMirror) {
const float RENDER_OPAQUE_BEYOND = 1.0f; // Meters beyond which body is shown opaque
const float RENDER_TRANSLUCENT_BEYOND = 0.5f;
// Render the body's voxels
_voxels.render(false);
// Render the body as balls and cones
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) {
float distanceToCamera = glm::length(_cameraPosition - _skeleton.joint[b].position);
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
float distanceToCamera = glm::length(_cameraPosition - _bodyBall[b].position);
float alpha = lookingInMirror ? 1.0f : glm::clamp((distanceToCamera - RENDER_TRANSLUCENT_BEYOND) /
(RENDER_OPAQUE_BEYOND - RENDER_TRANSLUCENT_BEYOND), 0.f, 1.f);
@ -1021,18 +1157,18 @@ 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);
}
} else if (_owningAgent || distanceToCamera > RENDER_TRANSLUCENT_BEYOND
|| b == AVATAR_JOINT_RIGHT_ELBOW
|| b == AVATAR_JOINT_RIGHT_WRIST
|| b == AVATAR_JOINT_RIGHT_FINGERTIPS ) {
// Render the sphere at the joint
if (_owningAgent || b == AVATAR_JOINT_RIGHT_ELBOW
|| b == AVATAR_JOINT_RIGHT_WRIST
|| b == AVATAR_JOINT_RIGHT_FINGERTIPS ) {
|| b == BODY_BALL_RIGHT_ELBOW
|| b == BODY_BALL_RIGHT_WRIST
|| b == BODY_BALL_RIGHT_FINGERTIPS ) {
// Render the body ball sphere
if (_owningAgent || b == BODY_BALL_RIGHT_ELBOW
|| b == BODY_BALL_RIGHT_WRIST
|| b == BODY_BALL_RIGHT_FINGERTIPS ) {
glColor3f(SKIN_COLOR[0] + _bodyBall[b].touchForce * 0.3f,
SKIN_COLOR[1] - _bodyBall[b].touchForce * 0.2f,
SKIN_COLOR[2] - _bodyBall[b].touchForce * 0.1f);
@ -1043,36 +1179,36 @@ void Avatar::renderBody(bool lookingInMirror) {
alpha);
}
if ((b != AVATAR_JOINT_HEAD_TOP )
&& (b != AVATAR_JOINT_HEAD_BASE )) {
if ((b != BODY_BALL_HEAD_TOP )
&& (b != BODY_BALL_HEAD_BASE )) {
glPushMatrix();
glTranslatef(_bodyBall[b].position.x, _bodyBall[b].position.y, _bodyBall[b].position.z);
glutSolidSphere(_bodyBall[b].radius, 20.0f, 20.0f);
glPopMatrix();
}
// Render the cone connecting this joint to its parent
if (_skeleton.joint[b].parent != AVATAR_JOINT_NULL) {
if ((b != AVATAR_JOINT_HEAD_TOP )
&& (b != AVATAR_JOINT_HEAD_BASE )
&& (b != AVATAR_JOINT_PELVIS )
&& (b != AVATAR_JOINT_TORSO )
&& (b != AVATAR_JOINT_CHEST )
&& (b != AVATAR_JOINT_LEFT_COLLAR )
&& (b != AVATAR_JOINT_LEFT_SHOULDER )
&& (b != AVATAR_JOINT_RIGHT_COLLAR )
&& (b != AVATAR_JOINT_RIGHT_SHOULDER)) {
// Render the cone connecting this ball to its parent
if (_bodyBall[b].parentBall != BODY_BALL_NULL) {
if ((b != BODY_BALL_HEAD_TOP )
&& (b != BODY_BALL_HEAD_BASE )
&& (b != BODY_BALL_PELVIS )
&& (b != BODY_BALL_TORSO )
&& (b != BODY_BALL_CHEST )
&& (b != BODY_BALL_LEFT_COLLAR )
&& (b != BODY_BALL_LEFT_SHOULDER )
&& (b != BODY_BALL_RIGHT_COLLAR )
&& (b != BODY_BALL_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,10 +1246,11 @@ 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);
}
}
<<<<<<< HEAD
void Avatar::loadData(QSettings* set) {
set->beginGroup("Avatar");
@ -1126,6 +1263,16 @@ void Avatar::loadData(QSettings* set) {
_position.z = set->value("position_z", _position.z).toFloat();
set->endGroup();
=======
void Avatar::getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const {
position = _bodyBall[jointID].position;
rotation = _bodyBall[jointID].rotation;
}
void Avatar::writeAvatarDataToFile() {
Application::getInstance()->setSetting("avatarPos", _position);
Application::getInstance()->setSetting("avatarRotation", glm::vec3(_bodyYaw, _bodyPitch, _bodyRoll));
>>>>>>> 82c1ee2062577f614cfde096f08adfc9e83e4f0f
}
void Avatar::saveData(QSettings* set) {

View file

@ -14,6 +14,7 @@
#include <QSettings>
#include "world.h"
#include "AvatarTouch.h"
#include "AvatarVoxelSystem.h"
#include "InterfaceConfig.h"
#include "SerialInterface.h"
#include "Balls.h"
@ -21,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,
@ -47,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);
@ -65,19 +101,15 @@ 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; }
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() * AVATAR_RIGHT; }
glm::vec3 getBodyUpDirection () const { return getOrientation() * AVATAR_UP; }
glm::vec3 getBodyFrontDirection () const { return getOrientation() * AVATAR_FRONT; }
const glm::vec3& getVelocity () const { return _velocity;}
float getSpeed () const { return _speed;}
float getHeight () const { return _height;}
@ -88,6 +120,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]; };
@ -96,9 +130,18 @@ public:
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
glm::vec3 getThrust() { return _thrust; };
<<<<<<< HEAD
// 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();
>>>>>>> 82c1ee2062577f614cfde096f08adfc9e83e4f0f
private:
// privatize copy constructor and assignment operator to avoid copying
@ -107,16 +150,22 @@ 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
};
Head _head;
Skeleton _skeleton;
bool _ballSpringsInitialized;
float _TEST_bigSphereRadius;
glm::vec3 _TEST_bigSpherePosition;
bool _mousePressed;
@ -124,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;
@ -149,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;

View file

@ -0,0 +1,261 @@
//
// 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();
// load the URL data asynchronously
if (!url.isValid()) {
return;
}
_voxelReply = Application::getInstance()->getNetworkAccessManager()->get(QNetworkRequest(url));
connect(_voxelReply, SIGNAL(readyRead()), SLOT(readVoxelDataFromReply()));
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::readVoxelDataFromReply() {
// for now, just wait until we have the full business
if (!_voxelReply->isFinished()) {
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;
}
}

View 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 readVoxelDataFromReply();
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__) */

View file

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

View file

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

View file

@ -72,6 +72,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 / PI;
}
// 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)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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.

View file

@ -71,7 +71,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);

View file

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

View file

@ -13,6 +13,7 @@
#define __hifi_VoxelConstants_h__
#include <limits.h>
#include <OctalCode.h>
const int TREE_SCALE = 128;
@ -23,11 +24,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

View file

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

View file

@ -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,

View file

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

View file

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