mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 07:37:31 +02:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
0bbea078b5
25 changed files with 803 additions and 209 deletions
|
@ -179,6 +179,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
||||||
_frameCount(0),
|
_frameCount(0),
|
||||||
_fps(120.0f),
|
_fps(120.0f),
|
||||||
_justStarted(true),
|
_justStarted(true),
|
||||||
|
_particleSystemInitialized(false),
|
||||||
|
_coolDemoParticleEmitter(-1),
|
||||||
_wantToKillLocalVoxels(false),
|
_wantToKillLocalVoxels(false),
|
||||||
_frustumDrawingMode(FRUSTUM_DRAW_MODE_ALL),
|
_frustumDrawingMode(FRUSTUM_DRAW_MODE_ALL),
|
||||||
_viewFrustumOffsetYaw(-135.0),
|
_viewFrustumOffsetYaw(-135.0),
|
||||||
|
@ -194,6 +196,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
||||||
_isTouchPressed(false),
|
_isTouchPressed(false),
|
||||||
_yawFromTouch(0.0f),
|
_yawFromTouch(0.0f),
|
||||||
_pitchFromTouch(0.0f),
|
_pitchFromTouch(0.0f),
|
||||||
|
_groundPlaneImpact(0.0f),
|
||||||
_mousePressed(false),
|
_mousePressed(false),
|
||||||
_mouseVoxelScale(1.0f / 1024.0f),
|
_mouseVoxelScale(1.0f / 1024.0f),
|
||||||
_justEditedVoxel(false),
|
_justEditedVoxel(false),
|
||||||
|
@ -1871,6 +1874,7 @@ void Application::init() {
|
||||||
_palette.addAction(_selectVoxelMode, 0, 4);
|
_palette.addAction(_selectVoxelMode, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const float MAX_AVATAR_EDIT_VELOCITY = 1.0f;
|
const float MAX_AVATAR_EDIT_VELOCITY = 1.0f;
|
||||||
const float MAX_VOXEL_EDIT_DISTANCE = 20.0f;
|
const float MAX_VOXEL_EDIT_DISTANCE = 20.0f;
|
||||||
const float HEAD_SPHERE_RADIUS = 0.07;
|
const float HEAD_SPHERE_RADIUS = 0.07;
|
||||||
|
@ -2056,6 +2060,7 @@ void Application::update(float deltaTime) {
|
||||||
// Leap finger-sensing device
|
// Leap finger-sensing device
|
||||||
LeapManager::enableFakeFingers(_simulateLeapHand->isChecked() || _testRaveGlove->isChecked());
|
LeapManager::enableFakeFingers(_simulateLeapHand->isChecked() || _testRaveGlove->isChecked());
|
||||||
LeapManager::nextFrame();
|
LeapManager::nextFrame();
|
||||||
|
_myAvatar.getHand().setRaveGloveActive(_testRaveGlove->isChecked());
|
||||||
_myAvatar.getHand().setLeapFingers(LeapManager::getFingerTips(), LeapManager::getFingerRoots());
|
_myAvatar.getHand().setLeapFingers(LeapManager::getFingerTips(), LeapManager::getFingerRoots());
|
||||||
_myAvatar.getHand().setLeapHands(LeapManager::getHandPositions(), LeapManager::getHandNormals());
|
_myAvatar.getHand().setLeapHands(LeapManager::getHandPositions(), LeapManager::getHandNormals());
|
||||||
|
|
||||||
|
@ -2103,6 +2108,8 @@ void Application::update(float deltaTime) {
|
||||||
_myAvatar.simulate(deltaTime, NULL);
|
_myAvatar.simulate(deltaTime, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_myAvatar.getHand().simulate(deltaTime, true);
|
||||||
|
|
||||||
if (!OculusManager::isConnected()) {
|
if (!OculusManager::isConnected()) {
|
||||||
if (_lookingInMirror->isChecked()) {
|
if (_lookingInMirror->isChecked()) {
|
||||||
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||||
|
@ -2151,7 +2158,7 @@ void Application::update(float deltaTime) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (TESTING_PARTICLE_SYSTEM) {
|
if (TESTING_PARTICLE_SYSTEM) {
|
||||||
_particleSystem.simulate(deltaTime);
|
updateParticleSystem(deltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2545,7 +2552,7 @@ void Application::displaySide(Camera& whichCamera) {
|
||||||
|
|
||||||
//draw a grid ground plane....
|
//draw a grid ground plane....
|
||||||
if (_renderGroundPlaneOn->isChecked()) {
|
if (_renderGroundPlaneOn->isChecked()) {
|
||||||
drawGroundPlaneGrid(EDGE_SIZE_GROUND_PLANE);
|
renderGroundPlaneGrid(EDGE_SIZE_GROUND_PLANE, _audio.getCollisionSoundMagnitude());
|
||||||
}
|
}
|
||||||
// Draw voxels
|
// Draw voxels
|
||||||
if (_renderVoxels->isChecked()) {
|
if (_renderVoxels->isChecked()) {
|
||||||
|
@ -2601,8 +2608,10 @@ void Application::displaySide(Camera& whichCamera) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TESTING_PARTICLE_SYSTEM) {
|
if (TESTING_PARTICLE_SYSTEM) {
|
||||||
|
if (_particleSystemInitialized) {
|
||||||
_particleSystem.render();
|
_particleSystem.render();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Render the world box
|
// Render the world box
|
||||||
if (!_lookingInMirror->isChecked() && _renderStatsOn->isChecked()) { render_world_box(); }
|
if (!_lookingInMirror->isChecked() && _renderStatsOn->isChecked()) { render_world_box(); }
|
||||||
|
@ -2621,6 +2630,9 @@ void Application::displayOverlay() {
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
glDisable(GL_LIGHTING);
|
glDisable(GL_LIGHTING);
|
||||||
|
|
||||||
|
// Display a single screen-size quad to
|
||||||
|
renderCollisionOverlay(_glWidget->width(), _glWidget->height(), _audio.getCollisionSoundMagnitude());
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
_audio.render(_glWidget->width(), _glWidget->height());
|
_audio.render(_glWidget->width(), _glWidget->height());
|
||||||
if (_oscilloscopeOn->isChecked()) {
|
if (_oscilloscopeOn->isChecked()) {
|
||||||
|
@ -3510,3 +3522,76 @@ void Application::exportSettings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Application::updateParticleSystem(float deltaTime) {
|
||||||
|
|
||||||
|
if (!_particleSystemInitialized) {
|
||||||
|
// create a stable test emitter and spit out a bunch of particles
|
||||||
|
_coolDemoParticleEmitter = _particleSystem.addEmitter();
|
||||||
|
|
||||||
|
if (_coolDemoParticleEmitter != -1) {
|
||||||
|
_particleSystem.setShowingEmitter(_coolDemoParticleEmitter, true);
|
||||||
|
glm::vec3 particleEmitterPosition = glm::vec3(5.0f, 1.0f, 5.0f);
|
||||||
|
_particleSystem.setEmitterPosition(_coolDemoParticleEmitter, particleEmitterPosition);
|
||||||
|
float radius = 0.01f;
|
||||||
|
glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glm::vec3 velocity(0.0f, 0.1f, 0.0f);
|
||||||
|
float lifespan = 100000.0f;
|
||||||
|
|
||||||
|
// determine a collision sphere
|
||||||
|
glm::vec3 collisionSpherePosition = glm::vec3( 5.0f, 0.5f, 5.0f );
|
||||||
|
float collisionSphereRadius = 0.5f;
|
||||||
|
_particleSystem.setCollisionSphere(_coolDemoParticleEmitter, collisionSpherePosition, collisionSphereRadius);
|
||||||
|
_particleSystem.emitParticlesNow(_coolDemoParticleEmitter, 1500, radius, color, velocity, lifespan);
|
||||||
|
}
|
||||||
|
|
||||||
|
// signal that the particle system has been initialized
|
||||||
|
_particleSystemInitialized = true;
|
||||||
|
|
||||||
|
// apply a preset color palette
|
||||||
|
_particleSystem.setOrangeBlueColorPalette();
|
||||||
|
} else {
|
||||||
|
// update the particle system
|
||||||
|
|
||||||
|
static float t = 0.0f;
|
||||||
|
t += deltaTime;
|
||||||
|
|
||||||
|
if (_coolDemoParticleEmitter != -1) {
|
||||||
|
|
||||||
|
glm::vec3 tilt = glm::vec3
|
||||||
|
(
|
||||||
|
30.0f * sinf( t * 0.55f ),
|
||||||
|
0.0f,
|
||||||
|
30.0f * cosf( t * 0.75f )
|
||||||
|
);
|
||||||
|
|
||||||
|
_particleSystem.setEmitterRotation(_coolDemoParticleEmitter, glm::quat(glm::radians(tilt)));
|
||||||
|
|
||||||
|
ParticleSystem::ParticleAttributes attributes;
|
||||||
|
|
||||||
|
attributes.gravity = 0.0f + 0.05f * sinf( t * 0.52f );
|
||||||
|
attributes.airFriction = 2.5 + 2.0f * sinf( t * 0.32f );
|
||||||
|
attributes.jitter = 0.05f + 0.05f * sinf( t * 0.42f );
|
||||||
|
attributes.emitterAttraction = 0.015f + 0.015f * cosf( t * 0.6f );
|
||||||
|
attributes.tornadoForce = 0.0f + 0.03f * sinf( t * 0.7f );
|
||||||
|
attributes.neighborAttraction = 0.1f + 0.1f * cosf( t * 0.8f );
|
||||||
|
attributes.neighborRepulsion = 0.2f + 0.2f * sinf( t * 0.4f );
|
||||||
|
attributes.bounce = 1.0f;
|
||||||
|
attributes.usingCollisionSphere = true;
|
||||||
|
attributes.collisionSpherePosition = glm::vec3( 5.0f, 0.5f, 5.0f );
|
||||||
|
attributes.collisionSphereRadius = 0.5f;
|
||||||
|
|
||||||
|
if (attributes.gravity < 0.0f) {
|
||||||
|
attributes.gravity = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
_particleSystem.setParticleAttributesForEmitter(_coolDemoParticleEmitter, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
_particleSystem.setUpDirection(glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
_particleSystem.simulate(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,10 @@ public:
|
||||||
|
|
||||||
const glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail _mouseVoxel);
|
const glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail _mouseVoxel);
|
||||||
|
|
||||||
|
void updateParticleSystem(float deltaTime);
|
||||||
|
|
||||||
Avatar* getAvatar() { return &_myAvatar; }
|
Avatar* getAvatar() { return &_myAvatar; }
|
||||||
|
Audio* getAudio() { return &_audio; }
|
||||||
Camera* getCamera() { return &_myCamera; }
|
Camera* getCamera() { return &_myCamera; }
|
||||||
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
|
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
|
||||||
VoxelSystem* getVoxels() { return &_voxels; }
|
VoxelSystem* getVoxels() { return &_voxels; }
|
||||||
|
@ -101,6 +104,9 @@ public:
|
||||||
QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
|
QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
|
||||||
GeometryCache* getGeometryCache() { return &_geometryCache; }
|
GeometryCache* getGeometryCache() { return &_geometryCache; }
|
||||||
|
|
||||||
|
void setGroundPlaneImpact(float groundPlaneImpact) { _groundPlaneImpact = groundPlaneImpact; }
|
||||||
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
void timer();
|
void timer();
|
||||||
|
@ -286,6 +292,8 @@ private:
|
||||||
timeval _timerStart, _timerEnd;
|
timeval _timerStart, _timerEnd;
|
||||||
timeval _lastTimeUpdated;
|
timeval _lastTimeUpdated;
|
||||||
bool _justStarted;
|
bool _justStarted;
|
||||||
|
bool _particleSystemInitialized;
|
||||||
|
int _coolDemoParticleEmitter;
|
||||||
|
|
||||||
Stars _stars;
|
Stars _stars;
|
||||||
|
|
||||||
|
@ -344,6 +352,8 @@ private:
|
||||||
float _yawFromTouch;
|
float _yawFromTouch;
|
||||||
float _pitchFromTouch;
|
float _pitchFromTouch;
|
||||||
|
|
||||||
|
float _groundPlaneImpact;
|
||||||
|
|
||||||
VoxelDetail _mouseVoxelDragging;
|
VoxelDetail _mouseVoxelDragging;
|
||||||
glm::vec3 _voxelThrust;
|
glm::vec3 _voxelThrust;
|
||||||
bool _mousePressed; // true if mouse has been pressed (clear when finished)
|
bool _mousePressed; // true if mouse has been pressed (clear when finished)
|
||||||
|
|
|
@ -77,8 +77,11 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
|
||||||
Application* interface = Application::getInstance();
|
Application* interface = Application::getInstance();
|
||||||
Avatar* interfaceAvatar = interface->getAvatar();
|
Avatar* interfaceAvatar = interface->getAvatar();
|
||||||
|
|
||||||
|
memset(outputLeft, 0, PACKET_LENGTH_BYTES_PER_CHANNEL);
|
||||||
|
memset(outputRight, 0, PACKET_LENGTH_BYTES_PER_CHANNEL);
|
||||||
|
|
||||||
// Add Procedural effects to input samples
|
// Add Procedural effects to input samples
|
||||||
addProceduralSounds(inputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
|
addProceduralSounds(inputLeft, outputLeft, outputRight, BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
|
||||||
|
|
||||||
if (nodeList && inputLeft) {
|
if (nodeList && inputLeft) {
|
||||||
|
|
||||||
|
@ -135,12 +138,8 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
|
||||||
+ leadingBytes);
|
+ leadingBytes);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(outputLeft, 0, PACKET_LENGTH_BYTES_PER_CHANNEL);
|
|
||||||
memset(outputRight, 0, PACKET_LENGTH_BYTES_PER_CHANNEL);
|
|
||||||
|
|
||||||
AudioRingBuffer* ringBuffer = &_ringBuffer;
|
AudioRingBuffer* ringBuffer = &_ringBuffer;
|
||||||
|
|
||||||
// if there is anything in the ring buffer, decide what to do:
|
// if there is anything in the ring buffer, decide what to do:
|
||||||
|
@ -251,11 +250,11 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifndef TEST_AUDIO_LOOPBACK
|
#ifndef TEST_AUDIO_LOOPBACK
|
||||||
outputLeft[s] = leftSample;
|
outputLeft[s] += leftSample;
|
||||||
outputRight[s] = rightSample;
|
outputRight[s] += rightSample;
|
||||||
#else
|
#else
|
||||||
outputLeft[s] = inputLeft[s];
|
outputLeft[s] += inputLeft[s];
|
||||||
outputRight[s] = inputLeft[s];
|
outputRight[s] += inputLeft[s];
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
ringBuffer->setNextOutput(ringBuffer->getNextOutput() + PACKET_LENGTH_SAMPLES);
|
ringBuffer->setNextOutput(ringBuffer->getNextOutput() + PACKET_LENGTH_SAMPLES);
|
||||||
|
@ -333,7 +332,13 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) :
|
||||||
_lastYawMeasuredMaximum(0),
|
_lastYawMeasuredMaximum(0),
|
||||||
_flangeIntensity(0.0f),
|
_flangeIntensity(0.0f),
|
||||||
_flangeRate(0.0f),
|
_flangeRate(0.0f),
|
||||||
_flangeWeight(0.0f)
|
_flangeWeight(0.0f),
|
||||||
|
_collisionSoundMagnitude(0.0f),
|
||||||
|
_collisionSoundFrequency(0.0f),
|
||||||
|
_collisionSoundNoise(0.0f),
|
||||||
|
_collisionSoundDuration(0.0f),
|
||||||
|
_proceduralEffectSample(0),
|
||||||
|
_heartbeatMagnitude(0.0f)
|
||||||
{
|
{
|
||||||
outputPortAudioError(Pa_Initialize());
|
outputPortAudioError(Pa_Initialize());
|
||||||
|
|
||||||
|
@ -589,7 +594,10 @@ void Audio::lowPassFilter(int16_t* inputBuffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take a pointer to the acquired microphone input samples and add procedural sounds
|
// Take a pointer to the acquired microphone input samples and add procedural sounds
|
||||||
void Audio::addProceduralSounds(int16_t* inputBuffer, int numSamples) {
|
void Audio::addProceduralSounds(int16_t* inputBuffer,
|
||||||
|
int16_t* outputLeft,
|
||||||
|
int16_t* outputRight,
|
||||||
|
int numSamples) {
|
||||||
const float MAX_AUDIBLE_VELOCITY = 6.0;
|
const float MAX_AUDIBLE_VELOCITY = 6.0;
|
||||||
const float MIN_AUDIBLE_VELOCITY = 0.1;
|
const float MIN_AUDIBLE_VELOCITY = 0.1;
|
||||||
const int VOLUME_BASELINE = 400;
|
const int VOLUME_BASELINE = 400;
|
||||||
|
@ -598,14 +606,48 @@ void Audio::addProceduralSounds(int16_t* inputBuffer, int numSamples) {
|
||||||
float speed = glm::length(_lastVelocity);
|
float speed = glm::length(_lastVelocity);
|
||||||
float volume = VOLUME_BASELINE * (1.f - speed / MAX_AUDIBLE_VELOCITY);
|
float volume = VOLUME_BASELINE * (1.f - speed / MAX_AUDIBLE_VELOCITY);
|
||||||
|
|
||||||
|
int sample;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Travelling noise
|
||||||
|
//
|
||||||
// Add a noise-modulated sinewave with volume that tapers off with speed increasing
|
// Add a noise-modulated sinewave with volume that tapers off with speed increasing
|
||||||
if ((speed > MIN_AUDIBLE_VELOCITY) && (speed < MAX_AUDIBLE_VELOCITY)) {
|
if ((speed > MIN_AUDIBLE_VELOCITY) && (speed < MAX_AUDIBLE_VELOCITY)) {
|
||||||
for (int i = 0; i < numSamples; i++) {
|
for (int i = 0; i < numSamples; i++) {
|
||||||
inputBuffer[i] += (int16_t)((sinf((float) i / SOUND_PITCH * speed) * randFloat()) * volume * speed);
|
inputBuffer[i] += (int16_t)(sinf((float) (_proceduralEffectSample + i) / SOUND_PITCH ) * volume * (1.f + randFloat() * 0.25f) * speed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const float COLLISION_SOUND_CUTOFF_LEVEL = 0.01f;
|
||||||
|
const float COLLISION_SOUND_MAX_VOLUME = 1000.f;
|
||||||
|
const float UP_MAJOR_FIFTH = powf(1.5f, 4.0f);
|
||||||
|
const float DOWN_TWO_OCTAVES = 4.f;
|
||||||
|
const float DOWN_FOUR_OCTAVES = 16.f;
|
||||||
|
float t;
|
||||||
|
if (_collisionSoundMagnitude > COLLISION_SOUND_CUTOFF_LEVEL) {
|
||||||
|
for (int i = 0; i < numSamples; i++) {
|
||||||
|
t = (float) _proceduralEffectSample + (float) i;
|
||||||
|
sample = sinf(t * _collisionSoundFrequency) +
|
||||||
|
sinf(t * _collisionSoundFrequency / DOWN_TWO_OCTAVES) +
|
||||||
|
sinf(t * _collisionSoundFrequency / DOWN_FOUR_OCTAVES * UP_MAJOR_FIFTH);
|
||||||
|
sample *= _collisionSoundMagnitude * COLLISION_SOUND_MAX_VOLUME;
|
||||||
|
inputBuffer[i] += sample;
|
||||||
|
outputLeft[i] += sample;
|
||||||
|
outputRight[i] += sample;
|
||||||
|
_collisionSoundMagnitude *= _collisionSoundDuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_proceduralEffectSample += numSamples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Starts a collision sound. magnitude is 0-1, with 1 the loudest possible sound.
|
||||||
|
//
|
||||||
|
void Audio::startCollisionSound(float magnitude, float frequency, float noise, float duration) {
|
||||||
|
_collisionSoundMagnitude = magnitude;
|
||||||
|
_collisionSoundFrequency = frequency;
|
||||||
|
_collisionSoundNoise = noise;
|
||||||
|
_collisionSoundDuration = duration;
|
||||||
|
}
|
||||||
// -----------------------------------------------------------
|
// -----------------------------------------------------------
|
||||||
// Accoustic ping (audio system round trip time determination)
|
// Accoustic ping (audio system round trip time determination)
|
||||||
// -----------------------------------------------------------
|
// -----------------------------------------------------------
|
||||||
|
|
|
@ -43,6 +43,10 @@ public:
|
||||||
|
|
||||||
void lowPassFilter(int16_t* inputBuffer);
|
void lowPassFilter(int16_t* inputBuffer);
|
||||||
|
|
||||||
|
void startCollisionSound(float magnitude, float frequency, float noise, float duration);
|
||||||
|
float getCollisionSoundMagnitude() { return _collisionSoundMagnitude; };
|
||||||
|
|
||||||
|
|
||||||
void ping();
|
void ping();
|
||||||
|
|
||||||
// Call periodically to eventually perform round trip time analysis,
|
// Call periodically to eventually perform round trip time analysis,
|
||||||
|
@ -80,6 +84,12 @@ private:
|
||||||
float _flangeIntensity;
|
float _flangeIntensity;
|
||||||
float _flangeRate;
|
float _flangeRate;
|
||||||
float _flangeWeight;
|
float _flangeWeight;
|
||||||
|
float _collisionSoundMagnitude;
|
||||||
|
float _collisionSoundFrequency;
|
||||||
|
float _collisionSoundNoise;
|
||||||
|
float _collisionSoundDuration;
|
||||||
|
int _proceduralEffectSample;
|
||||||
|
float _heartbeatMagnitude;
|
||||||
|
|
||||||
// Audio callback in class context.
|
// Audio callback in class context.
|
||||||
inline void performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight);
|
inline void performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight);
|
||||||
|
@ -92,7 +102,7 @@ private:
|
||||||
inline void analyzePing();
|
inline void analyzePing();
|
||||||
|
|
||||||
// Add sounds that we want the user to not hear themselves, by adding on top of mic input signal
|
// Add sounds that we want the user to not hear themselves, by adding on top of mic input signal
|
||||||
void addProceduralSounds(int16_t* inputBuffer, int numSamples);
|
void addProceduralSounds(int16_t* inputBuffer, int16_t* outputLeft, int16_t* outputRight, int numSamples);
|
||||||
|
|
||||||
|
|
||||||
// Audio callback called by portaudio. Calls 'performIO'.
|
// Audio callback called by portaudio. Calls 'performIO'.
|
||||||
|
|
|
@ -546,8 +546,8 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
||||||
_position += _scale * _gravity * (GRAVITY_EARTH * deltaTime) * deltaTime;
|
_position += _scale * _gravity * (GRAVITY_EARTH * deltaTime) * deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCollisionWithEnvironment();
|
updateCollisionWithEnvironment(deltaTime);
|
||||||
updateCollisionWithVoxels();
|
updateCollisionWithVoxels(deltaTime);
|
||||||
updateAvatarCollisions(deltaTime);
|
updateAvatarCollisions(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -819,6 +819,12 @@ void Avatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMovem
|
||||||
} else {
|
} else {
|
||||||
_avatarTouch.setHasInteractingOther(false);
|
_avatarTouch.setHasInteractingOther(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there's a leap-interaction hand visible, use that as the endpoint
|
||||||
|
if (getHand().getHandPositions().size() > 0) {
|
||||||
|
_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position =
|
||||||
|
getHand().leapPositionToWorldPosition(getHand().getHandPositions()[0]);
|
||||||
|
}
|
||||||
}//if (_isMine)
|
}//if (_isMine)
|
||||||
|
|
||||||
//constrain right arm length and re-adjust elbow position as it bends
|
//constrain right arm length and re-adjust elbow position as it bends
|
||||||
|
@ -868,29 +874,41 @@ void Avatar::updateCollisionWithSphere(glm::vec3 position, float radius, float d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avatar::updateCollisionWithEnvironment() {
|
void Avatar::updateCollisionWithEnvironment(float deltaTime) {
|
||||||
|
|
||||||
glm::vec3 up = getBodyUpDirection();
|
glm::vec3 up = getBodyUpDirection();
|
||||||
float radius = _height * 0.125f;
|
float radius = _height * 0.125f;
|
||||||
const float ENVIRONMENT_SURFACE_ELASTICITY = 1.0f;
|
const float ENVIRONMENT_SURFACE_ELASTICITY = 1.0f;
|
||||||
const float ENVIRONMENT_SURFACE_DAMPING = 0.01;
|
const float ENVIRONMENT_SURFACE_DAMPING = 0.01;
|
||||||
|
const float ENVIRONMENT_COLLISION_FREQUENCY = 0.05f;
|
||||||
|
const float VISIBLE_GROUND_COLLISION_VELOCITY = 0.2f;
|
||||||
glm::vec3 penetration;
|
glm::vec3 penetration;
|
||||||
if (Application::getInstance()->getEnvironment()->findCapsulePenetration(
|
if (Application::getInstance()->getEnvironment()->findCapsulePenetration(
|
||||||
_position - up * (_pelvisFloatingHeight - radius),
|
_position - up * (_pelvisFloatingHeight - radius),
|
||||||
_position + up * (_height - _pelvisFloatingHeight - radius), radius, penetration)) {
|
_position + up * (_height - _pelvisFloatingHeight - radius), radius, penetration)) {
|
||||||
|
float velocityTowardCollision = glm::dot(_velocity, glm::normalize(penetration));
|
||||||
|
if (velocityTowardCollision > VISIBLE_GROUND_COLLISION_VELOCITY) {
|
||||||
|
Application::getInstance()->setGroundPlaneImpact(1.0f);
|
||||||
|
}
|
||||||
|
updateCollisionSound(penetration, deltaTime, ENVIRONMENT_COLLISION_FREQUENCY);
|
||||||
|
|
||||||
applyHardCollision(penetration, ENVIRONMENT_SURFACE_ELASTICITY, ENVIRONMENT_SURFACE_DAMPING);
|
applyHardCollision(penetration, ENVIRONMENT_SURFACE_ELASTICITY, ENVIRONMENT_SURFACE_DAMPING);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Avatar::updateCollisionWithVoxels() {
|
void Avatar::updateCollisionWithVoxels(float deltaTime) {
|
||||||
float radius = _height * 0.125f;
|
float radius = _height * 0.125f;
|
||||||
const float VOXEL_ELASTICITY = 1.4f;
|
const float VOXEL_ELASTICITY = 1.4f;
|
||||||
const float VOXEL_DAMPING = 0.0;
|
const float VOXEL_DAMPING = 0.0;
|
||||||
|
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
|
||||||
glm::vec3 penetration;
|
glm::vec3 penetration;
|
||||||
if (Application::getInstance()->getVoxels()->findCapsulePenetration(
|
if (Application::getInstance()->getVoxels()->findCapsulePenetration(
|
||||||
_position - glm::vec3(0.0f, _pelvisFloatingHeight - radius, 0.0f),
|
_position - glm::vec3(0.0f, _pelvisFloatingHeight - radius, 0.0f),
|
||||||
_position + glm::vec3(0.0f, _height - _pelvisFloatingHeight - radius, 0.0f), radius, penetration)) {
|
_position + glm::vec3(0.0f, _height - _pelvisFloatingHeight - radius, 0.0f), radius, penetration)) {
|
||||||
|
updateCollisionSound(penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
|
||||||
applyHardCollision(penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
|
applyHardCollision(penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -919,6 +937,36 @@ void Avatar::applyHardCollision(const glm::vec3& penetration, float elasticity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Avatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) {
|
||||||
|
// consider whether to have the collision make a sound
|
||||||
|
const float AUDIBLE_COLLISION_THRESHOLD = 0.02f;
|
||||||
|
const float COLLISION_LOUDNESS = 1.f;
|
||||||
|
const float DURATION_SCALING = 0.004f;
|
||||||
|
const float NOISE_SCALING = 0.1f;
|
||||||
|
glm::vec3 velocity = _velocity;
|
||||||
|
glm::vec3 gravity = getGravity();
|
||||||
|
|
||||||
|
if (glm::length(gravity) > EPSILON) {
|
||||||
|
// If gravity is on, remove the effect of gravity on velocity for this
|
||||||
|
// frame, so that we are not constantly colliding with the surface
|
||||||
|
velocity -= _scale * glm::length(gravity) * GRAVITY_EARTH * deltaTime * glm::normalize(gravity);
|
||||||
|
}
|
||||||
|
float velocityTowardCollision = glm::dot(velocity, glm::normalize(penetration));
|
||||||
|
float velocityTangentToCollision = glm::length(velocity) - velocityTowardCollision;
|
||||||
|
|
||||||
|
if (velocityTowardCollision > AUDIBLE_COLLISION_THRESHOLD) {
|
||||||
|
// Volume is proportional to collision velocity
|
||||||
|
// Base frequency is modified upward by the angle of the collision
|
||||||
|
// Noise is a function of the angle of collision
|
||||||
|
// Duration of the sound is a function of both base frequency and velocity of impact
|
||||||
|
Application::getInstance()->getAudio()->startCollisionSound(
|
||||||
|
fmin(COLLISION_LOUDNESS * velocityTowardCollision, 1.f),
|
||||||
|
frequency * (1.f + velocityTangentToCollision / velocityTowardCollision),
|
||||||
|
fmin(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.f),
|
||||||
|
1.f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Avatar::updateAvatarCollisions(float deltaTime) {
|
void Avatar::updateAvatarCollisions(float deltaTime) {
|
||||||
|
|
||||||
// Reset detector for nearest avatar
|
// Reset detector for nearest avatar
|
||||||
|
|
|
@ -162,6 +162,8 @@ public:
|
||||||
glm::quat getOrientation () const;
|
glm::quat getOrientation () const;
|
||||||
glm::quat getWorldAlignedOrientation() const;
|
glm::quat getWorldAlignedOrientation() const;
|
||||||
|
|
||||||
|
glm::vec3 getGravity () const { return _gravity; }
|
||||||
|
|
||||||
glm::vec3 getUprightHeadPosition() const;
|
glm::vec3 getUprightHeadPosition() const;
|
||||||
|
|
||||||
AvatarVoxelSystem* getVoxels() { return &_voxels; }
|
AvatarVoxelSystem* getVoxels() { return &_voxels; }
|
||||||
|
@ -262,9 +264,10 @@ private:
|
||||||
void updateAvatarCollisions(float deltaTime);
|
void updateAvatarCollisions(float deltaTime);
|
||||||
void updateArmIKAndConstraints( float deltaTime );
|
void updateArmIKAndConstraints( float deltaTime );
|
||||||
void updateCollisionWithSphere( glm::vec3 position, float radius, float deltaTime );
|
void updateCollisionWithSphere( glm::vec3 position, float radius, float deltaTime );
|
||||||
void updateCollisionWithEnvironment();
|
void updateCollisionWithEnvironment(float deltaTime);
|
||||||
void updateCollisionWithVoxels();
|
void updateCollisionWithVoxels(float deltaTime);
|
||||||
void applyHardCollision(const glm::vec3& penetration, float elasticity, float damping);
|
void applyHardCollision(const glm::vec3& penetration, float elasticity, float damping);
|
||||||
|
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
|
||||||
void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime );
|
void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime );
|
||||||
void checkForMouseRayTouching();
|
void checkForMouseRayTouching();
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,8 +23,13 @@ Hand::Hand(Avatar* owningAvatar) :
|
||||||
_lookingInMirror(false),
|
_lookingInMirror(false),
|
||||||
_ballColor(0.0, 0.0, 0.4),
|
_ballColor(0.0, 0.0, 0.4),
|
||||||
_position(0.0, 0.4, 0.0),
|
_position(0.0, 0.4, 0.0),
|
||||||
_orientation(0.0, 0.0, 0.0, 1.0)
|
_orientation(0.0, 0.0, 0.0, 1.0),
|
||||||
|
_particleSystemInitialized(false)
|
||||||
{
|
{
|
||||||
|
// initialize all finger particle emitters with an invalid id as default
|
||||||
|
for (int f = 0; f< NUM_FINGERS_PER_HAND; f ++ ) {
|
||||||
|
_fingerParticleEmitter[f] = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hand::init() {
|
void Hand::init() {
|
||||||
|
@ -40,6 +45,7 @@ void Hand::reset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hand::simulate(float deltaTime, bool isMine) {
|
void Hand::simulate(float deltaTime, bool isMine) {
|
||||||
|
updateFingerParticles(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 Hand::leapPositionToWorldPosition(const glm::vec3& leapPosition) {
|
glm::vec3 Hand::leapPositionToWorldPosition(const glm::vec3& leapPosition) {
|
||||||
|
@ -69,17 +75,52 @@ void Hand::calculateGeometry() {
|
||||||
|
|
||||||
void Hand::render(bool lookingInMirror) {
|
void Hand::render(bool lookingInMirror) {
|
||||||
|
|
||||||
|
if (_particleSystemInitialized) {
|
||||||
|
_particleSystem.render();
|
||||||
|
}
|
||||||
|
|
||||||
_renderAlpha = 1.0;
|
_renderAlpha = 1.0;
|
||||||
_lookingInMirror = lookingInMirror;
|
_lookingInMirror = lookingInMirror;
|
||||||
|
|
||||||
calculateGeometry();
|
calculateGeometry();
|
||||||
|
|
||||||
|
if (_isRaveGloveActive)
|
||||||
|
renderRaveGloveStage();
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
glEnable(GL_RESCALE_NORMAL);
|
glEnable(GL_RESCALE_NORMAL);
|
||||||
|
|
||||||
renderHandSpheres();
|
renderHandSpheres();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Hand::renderRaveGloveStage() {
|
||||||
|
if (_owningAvatar && _owningAvatar->isMyAvatar()) {
|
||||||
|
Head& head = _owningAvatar->getHead();
|
||||||
|
glm::quat headOrientation = head.getOrientation();
|
||||||
|
glm::vec3 headPosition = head.getPosition();
|
||||||
|
float scale = 100.0f;
|
||||||
|
glm::vec3 vc = headOrientation * glm::vec3( 0.0f, 0.0f, -30.0f) + headPosition;
|
||||||
|
glm::vec3 v0 = headOrientation * (glm::vec3(-1.0f, -1.0f, 0.0f) * scale) + vc;
|
||||||
|
glm::vec3 v1 = headOrientation * (glm::vec3( 1.0f, -1.0f, 0.0f) * scale) + vc;
|
||||||
|
glm::vec3 v2 = headOrientation * (glm::vec3( 1.0f, 1.0f, 0.0f) * scale) + vc;
|
||||||
|
glm::vec3 v3 = headOrientation * (glm::vec3(-1.0f, 1.0f, 0.0f) * scale) + vc;
|
||||||
|
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBegin(GL_TRIANGLE_FAN);
|
||||||
|
glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glVertex3fv((float*)&vc);
|
||||||
|
glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
|
||||||
|
glVertex3fv((float*)&v0);
|
||||||
|
glVertex3fv((float*)&v1);
|
||||||
|
glVertex3fv((float*)&v2);
|
||||||
|
glVertex3fv((float*)&v3);
|
||||||
|
glVertex3fv((float*)&v0);
|
||||||
|
glEnd();
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Hand::renderHandSpheres() {
|
void Hand::renderHandSpheres() {
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
// Draw the leap balls
|
// Draw the leap balls
|
||||||
|
@ -132,3 +173,52 @@ void Hand::setLeapHands(const std::vector<glm::vec3>& handPositions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Hand::updateFingerParticles(float deltaTime) {
|
||||||
|
|
||||||
|
if (!_particleSystemInitialized) {
|
||||||
|
for ( int f = 0; f< NUM_FINGERS_PER_HAND; f ++ ) {
|
||||||
|
_fingerParticleEmitter[f] = _particleSystem.addEmitter();
|
||||||
|
_particleSystem.setShowingEmitter(_fingerParticleEmitter[f], true);
|
||||||
|
}
|
||||||
|
_particleSystemInitialized = true;
|
||||||
|
} else {
|
||||||
|
// update the particles
|
||||||
|
|
||||||
|
static float t = 0.0f;
|
||||||
|
t += deltaTime;
|
||||||
|
|
||||||
|
for ( int f = 0; f< _fingerTips.size(); f ++ ) {
|
||||||
|
|
||||||
|
if (_fingerParticleEmitter[f] != -1) {
|
||||||
|
|
||||||
|
glm::vec3 particleEmitterPosition = leapPositionToWorldPosition(_fingerTips[f]);
|
||||||
|
|
||||||
|
// this aspect is still being designed....
|
||||||
|
|
||||||
|
glm::vec3 tilt = glm::vec3
|
||||||
|
(
|
||||||
|
30.0f * sinf( t * 0.55f ),
|
||||||
|
0.0f,
|
||||||
|
30.0f * cosf( t * 0.75f )
|
||||||
|
);
|
||||||
|
|
||||||
|
glm::quat particleEmitterRotation = glm::quat(glm::radians(tilt));
|
||||||
|
|
||||||
|
_particleSystem.setEmitterPosition(_fingerParticleEmitter[0], particleEmitterPosition);
|
||||||
|
_particleSystem.setEmitterRotation(_fingerParticleEmitter[0], particleEmitterRotation);
|
||||||
|
|
||||||
|
float radius = 0.005f;
|
||||||
|
glm::vec4 color(1.0f, 0.6f, 0.0f, 0.5f);
|
||||||
|
glm::vec3 velocity(0.0f, 0.005f, 0.0f);
|
||||||
|
float lifespan = 0.3f;
|
||||||
|
_particleSystem.emitParticlesNow(_fingerParticleEmitter[0], 1, radius, color, velocity, lifespan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_particleSystem.setUpDirection(glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
_particleSystem.simulate(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,11 @@
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
#include "SerialInterface.h"
|
#include "SerialInterface.h"
|
||||||
|
#include "ParticleSystem.h"
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
const int NUM_FINGERS_PER_HAND = 5;
|
||||||
|
|
||||||
class Avatar;
|
class Avatar;
|
||||||
class ProgramObject;
|
class ProgramObject;
|
||||||
|
@ -46,9 +48,13 @@ public:
|
||||||
const std::vector<glm::vec3>& fingerRoots);
|
const std::vector<glm::vec3>& fingerRoots);
|
||||||
void setLeapHands (const std::vector<glm::vec3>& handPositions,
|
void setLeapHands (const std::vector<glm::vec3>& handPositions,
|
||||||
const std::vector<glm::vec3>& handNormals);
|
const std::vector<glm::vec3>& handNormals);
|
||||||
|
void updateFingerParticles(float deltaTime);
|
||||||
|
void setRaveGloveActive(bool active) { _isRaveGloveActive = active; }
|
||||||
|
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
const glm::vec3& getLeapBallPosition (int ball) const { return _leapBalls[ball].position;}
|
const glm::vec3& getLeapBallPosition (int ball) const { return _leapBalls[ball].position;}
|
||||||
|
bool isRaveGloveActive () const { return _isRaveGloveActive; }
|
||||||
|
|
||||||
// position conversion
|
// position conversion
|
||||||
glm::vec3 leapPositionToWorldPosition(const glm::vec3& leapPosition);
|
glm::vec3 leapPositionToWorldPosition(const glm::vec3& leapPosition);
|
||||||
|
@ -58,15 +64,22 @@ private:
|
||||||
Hand(const Hand&);
|
Hand(const Hand&);
|
||||||
Hand& operator= (const Hand&);
|
Hand& operator= (const Hand&);
|
||||||
|
|
||||||
|
ParticleSystem _particleSystem;
|
||||||
|
|
||||||
Avatar* _owningAvatar;
|
Avatar* _owningAvatar;
|
||||||
float _renderAlpha;
|
float _renderAlpha;
|
||||||
bool _lookingInMirror;
|
bool _lookingInMirror;
|
||||||
|
bool _isRaveGloveActive;
|
||||||
glm::vec3 _ballColor;
|
glm::vec3 _ballColor;
|
||||||
glm::vec3 _position;
|
glm::vec3 _position;
|
||||||
glm::quat _orientation;
|
glm::quat _orientation;
|
||||||
std::vector<HandBall> _leapBalls;
|
std::vector<HandBall> _leapBalls;
|
||||||
|
|
||||||
|
bool _particleSystemInitialized;
|
||||||
|
int _fingerParticleEmitter[NUM_FINGERS_PER_HAND];
|
||||||
|
|
||||||
// private methods
|
// private methods
|
||||||
|
void renderRaveGloveStage();
|
||||||
void renderHandSpheres();
|
void renderHandSpheres();
|
||||||
void calculateGeometry();
|
void calculateGeometry();
|
||||||
};
|
};
|
||||||
|
|
|
@ -227,7 +227,8 @@ void Head::simulate(float deltaTime, bool isMine) {
|
||||||
const float CAMERA_FOLLOW_HEAD_RATE_MAX = 0.5f;
|
const float CAMERA_FOLLOW_HEAD_RATE_MAX = 0.5f;
|
||||||
const float CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE = 1.05f;
|
const float CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE = 1.05f;
|
||||||
const float CAMERA_STOP_TOLERANCE_DEGREES = 0.1f;
|
const float CAMERA_STOP_TOLERANCE_DEGREES = 0.1f;
|
||||||
const float CAMERA_START_TOLERANCE_DEGREES = 2.0f;
|
const float CAMERA_PITCH_START_TOLERANCE_DEGREES = 10.0f;
|
||||||
|
const float CAMERA_YAW_START_TOLERANCE_DEGREES = 3.0f;
|
||||||
float cameraHeadAngleDifference = glm::length(glm::vec2(_pitch - _cameraPitch, _yaw - _cameraYaw));
|
float cameraHeadAngleDifference = glm::length(glm::vec2(_pitch - _cameraPitch, _yaw - _cameraYaw));
|
||||||
if (_isCameraMoving) {
|
if (_isCameraMoving) {
|
||||||
_cameraFollowHeadRate = glm::clamp(_cameraFollowHeadRate * CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE,
|
_cameraFollowHeadRate = glm::clamp(_cameraFollowHeadRate * CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE,
|
||||||
|
@ -240,7 +241,8 @@ void Head::simulate(float deltaTime, bool isMine) {
|
||||||
_isCameraMoving = false;
|
_isCameraMoving = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (cameraHeadAngleDifference > CAMERA_START_TOLERANCE_DEGREES) {
|
if ((fabs(_pitch - _cameraPitch) > CAMERA_PITCH_START_TOLERANCE_DEGREES) ||
|
||||||
|
(fabs(_yaw - _cameraYaw) > CAMERA_YAW_START_TOLERANCE_DEGREES)) {
|
||||||
_isCameraMoving = true;
|
_isCameraMoving = true;
|
||||||
_cameraFollowHeadRate = CAMERA_FOLLOW_HEAD_RATE_START;
|
_cameraFollowHeadRate = CAMERA_FOLLOW_HEAD_RATE_START;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,74 +8,157 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
#include "ParticleSystem.h"
|
#include "ParticleSystem.h"
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
|
const float DEFAULT_PARTICLE_BOUNCE = 1.0f;
|
||||||
|
const float DEFAULT_PARTICLE_AIR_FRICTION = 2.0f;
|
||||||
|
|
||||||
ParticleSystem::ParticleSystem() {
|
ParticleSystem::ParticleSystem() {
|
||||||
|
|
||||||
_numberOfParticles = 1500;
|
|
||||||
assert(_numberOfParticles <= MAX_PARTICLES);
|
|
||||||
|
|
||||||
_bounce = 0.9f;
|
|
||||||
_timer = 0.0f;
|
_timer = 0.0f;
|
||||||
_airFriction = 6.0f;
|
_numEmitters = 0;
|
||||||
_jitter = 0.1f;
|
_numParticles = 0;
|
||||||
_homeAttraction = 0.0f;
|
_upDirection = glm::vec3(0.0f, 1.0f, 0.0f); // default
|
||||||
_tornadoForce = 0.0f;
|
|
||||||
_neighborAttraction = 0.02f;
|
|
||||||
_neighborRepulsion = 0.9f;
|
|
||||||
_tornadoAxis = glm::normalize(glm::vec3(0.1f, 1.0f, 0.1f));
|
|
||||||
_home = glm::vec3(5.0f, 1.0f, 5.0f);
|
|
||||||
|
|
||||||
_TEST_bigSphereRadius = 0.5f;
|
for (unsigned int e = 0; e < MAX_EMITTERS; e++) {
|
||||||
_TEST_bigSpherePosition = glm::vec3( 5.0f, _TEST_bigSphereRadius, 5.0f);
|
_emitter[e].position = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||||
|
_emitter[e].rotation = glm::quat();
|
||||||
|
_emitter[e].right = IDENTITY_RIGHT;
|
||||||
|
_emitter[e].up = IDENTITY_UP;
|
||||||
|
_emitter[e].front = IDENTITY_FRONT;
|
||||||
|
_emitter[e].showingEmitter = false;
|
||||||
|
_emitter[e].particleAttributes.bounce = DEFAULT_PARTICLE_BOUNCE;
|
||||||
|
_emitter[e].particleAttributes.airFriction = DEFAULT_PARTICLE_AIR_FRICTION;
|
||||||
|
_emitter[e].particleAttributes.gravity = 0.0f;
|
||||||
|
_emitter[e].particleAttributes.jitter = 0.0f;
|
||||||
|
_emitter[e].particleAttributes.emitterAttraction = 0.0f;
|
||||||
|
_emitter[e].particleAttributes.tornadoForce = 0.0f;
|
||||||
|
_emitter[e].particleAttributes.neighborAttraction = 0.0f;
|
||||||
|
_emitter[e].particleAttributes.neighborRepulsion = 0.0f;
|
||||||
|
_emitter[e].particleAttributes.collisionSphereRadius = 0.0f;
|
||||||
|
_emitter[e].particleAttributes.collisionSpherePosition = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||||
|
_emitter[e].particleAttributes.usingCollisionSphere = false;
|
||||||
|
};
|
||||||
|
|
||||||
for (unsigned int p = 0; p < _numberOfParticles; p++) {
|
for (unsigned int p = 0; p < MAX_PARTICLES; p++) {
|
||||||
_particle[p].position = _home;
|
_particle[p].alive = false;
|
||||||
|
_particle[p].age = 0.0f;
|
||||||
|
_particle[p].lifespan = 0.0f;
|
||||||
|
_particle[p].radius = 0.0f;
|
||||||
|
_particle[p].emitterIndex = 0;
|
||||||
|
_particle[p].position = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||||
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
|
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float radian = ((float)p / (float)_numberOfParticles) * PI_TIMES_TWO;
|
|
||||||
|
int ParticleSystem::addEmitter() {
|
||||||
|
|
||||||
|
_numEmitters ++;
|
||||||
|
|
||||||
|
if (_numEmitters > MAX_EMITTERS) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _numEmitters - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ParticleSystem::simulate(float deltaTime) {
|
||||||
|
|
||||||
|
// update emitters
|
||||||
|
for (unsigned int e = 0; e < _numEmitters; e++) {
|
||||||
|
updateEmitter(e, deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update particles
|
||||||
|
for (unsigned int p = 0; p < _numParticles; p++) {
|
||||||
|
if (_particle[p].alive) {
|
||||||
|
updateParticle(p, deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::updateEmitter(int e, float deltaTime) {
|
||||||
|
|
||||||
|
_emitter[e].front = _emitter[e].rotation * IDENTITY_FRONT;
|
||||||
|
_emitter[e].right = _emitter[e].rotation * IDENTITY_RIGHT;
|
||||||
|
_emitter[e].up = _emitter[e].rotation * IDENTITY_UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ParticleSystem::emitParticlesNow(int e, int num, float radius, glm::vec4 color, glm::vec3 velocity, float lifespan) {
|
||||||
|
|
||||||
|
for (unsigned int p = 0; p < num; p++) {
|
||||||
|
createParticle(e, _emitter[e].position, velocity, radius, color, lifespan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::createParticle(int e, glm::vec3 position, glm::vec3 velocity, float radius, glm::vec4 color, float lifespan) {
|
||||||
|
|
||||||
|
for (unsigned int p = 0; p < MAX_PARTICLES; p++) {
|
||||||
|
if (!_particle[p].alive) {
|
||||||
|
|
||||||
|
_particle[p].emitterIndex = e;
|
||||||
|
_particle[p].lifespan = lifespan;
|
||||||
|
_particle[p].alive = true;
|
||||||
|
_particle[p].age = 0.0f;
|
||||||
|
_particle[p].position = position;
|
||||||
|
_particle[p].velocity = velocity;
|
||||||
|
_particle[p].radius = radius;
|
||||||
|
_particle[p].color = color;
|
||||||
|
|
||||||
|
_numParticles ++;
|
||||||
|
|
||||||
|
assert(_numParticles <= MAX_PARTICLES);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::killParticle(int p) {
|
||||||
|
|
||||||
|
assert( p >= 0);
|
||||||
|
assert( p < MAX_PARTICLES);
|
||||||
|
assert( _numParticles > 0);
|
||||||
|
|
||||||
|
_particle[p].alive = false;
|
||||||
|
_numParticles --;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ParticleSystem::setOrangeBlueColorPalette() {
|
||||||
|
|
||||||
|
for (unsigned int p = 0; p < _numParticles; p++) {
|
||||||
|
|
||||||
|
float radian = ((float)p / (float)_numParticles) * PI_TIMES_TWO;
|
||||||
float wave = sinf(radian);
|
float wave = sinf(radian);
|
||||||
|
|
||||||
float red = 0.5f + 0.5f * wave;
|
float red = 0.5f + 0.5f * wave;
|
||||||
float green = 0.3f + 0.3f * wave;
|
float green = 0.3f + 0.3f * wave;
|
||||||
float blue = 0.2f - 0.2f * wave;
|
float blue = 0.2f - 0.2f * wave;
|
||||||
|
float alpha = 1.0f;
|
||||||
|
|
||||||
_particle[p].color = glm::vec3(red, green, blue);
|
_particle[p].color = glm::vec4(red, green, blue, alpha);
|
||||||
_particle[p].age = 0.0f;
|
|
||||||
_particle[p].radius = 0.01f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParticleSystem::simulate(float deltaTime) {
|
|
||||||
|
|
||||||
runSpecialEffectsTest(deltaTime);
|
|
||||||
|
|
||||||
for (unsigned int p = 0; p < _numberOfParticles; p++) {
|
|
||||||
updateParticle(p, deltaTime);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ParticleSystem::setParticleAttributesForEmitter(int emitterIndex, ParticleAttributes attributes) {
|
||||||
|
|
||||||
void ParticleSystem::runSpecialEffectsTest(float deltaTime) {
|
_emitter[emitterIndex].particleAttributes.bounce = attributes.bounce;
|
||||||
|
_emitter[emitterIndex].particleAttributes.gravity = attributes.gravity;
|
||||||
_timer += deltaTime;
|
_emitter[emitterIndex].particleAttributes.airFriction = attributes.airFriction;
|
||||||
|
_emitter[emitterIndex].particleAttributes.jitter = attributes.jitter;
|
||||||
_gravity = 0.01f + 0.01f * sinf( _timer * 0.52f );
|
_emitter[emitterIndex].particleAttributes.emitterAttraction = attributes.emitterAttraction;
|
||||||
_airFriction = 3.0f + 2.0f * sinf( _timer * 0.32f );
|
_emitter[emitterIndex].particleAttributes.tornadoForce = attributes.tornadoForce;
|
||||||
_jitter = 0.05f + 0.05f * sinf( _timer * 0.42f );
|
_emitter[emitterIndex].particleAttributes.neighborAttraction = attributes.neighborAttraction;
|
||||||
_homeAttraction = 0.01f + 0.01f * cosf( _timer * 0.6f );
|
_emitter[emitterIndex].particleAttributes.neighborRepulsion = attributes.neighborRepulsion;
|
||||||
_tornadoForce = 0.0f + 0.03f * sinf( _timer * 0.7f );
|
_emitter[emitterIndex].particleAttributes.usingCollisionSphere = attributes.usingCollisionSphere;
|
||||||
_neighborAttraction = 0.1f + 0.1f * cosf( _timer * 0.8f );
|
_emitter[emitterIndex].particleAttributes.collisionSpherePosition = attributes.collisionSpherePosition;
|
||||||
_neighborRepulsion = 0.4f + 0.3f * sinf( _timer * 0.4f );
|
_emitter[emitterIndex].particleAttributes.collisionSphereRadius = attributes.collisionSphereRadius;
|
||||||
|
|
||||||
_tornadoAxis = glm::vec3
|
|
||||||
(
|
|
||||||
0.0f + 0.5f * sinf( _timer * 0.55f ),
|
|
||||||
1.0f,
|
|
||||||
0.0f + 0.5f * cosf( _timer * 0.75f )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,40 +167,48 @@ void ParticleSystem::updateParticle(int p, float deltaTime) {
|
||||||
|
|
||||||
_particle[p].age += deltaTime;
|
_particle[p].age += deltaTime;
|
||||||
|
|
||||||
|
if (_particle[p].age > _particle[p].lifespan) {
|
||||||
|
killParticle(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
Emitter myEmitter = _emitter[_particle[p].emitterIndex];
|
||||||
|
|
||||||
// apply random jitter
|
// apply random jitter
|
||||||
_particle[p].velocity +=
|
_particle[p].velocity +=
|
||||||
glm::vec3
|
glm::vec3
|
||||||
(
|
(
|
||||||
-_jitter * ONE_HALF + _jitter * randFloat(),
|
-myEmitter.particleAttributes.jitter * ONE_HALF + myEmitter.particleAttributes.jitter * randFloat(),
|
||||||
-_jitter * ONE_HALF + _jitter * randFloat(),
|
-myEmitter.particleAttributes.jitter * ONE_HALF + myEmitter.particleAttributes.jitter * randFloat(),
|
||||||
-_jitter * ONE_HALF + _jitter * randFloat()
|
-myEmitter.particleAttributes.jitter * ONE_HALF + myEmitter.particleAttributes.jitter * randFloat()
|
||||||
) * deltaTime;
|
) * deltaTime;
|
||||||
|
|
||||||
|
|
||||||
// apply attraction to home position
|
// apply attraction to home position
|
||||||
glm::vec3 vectorToHome = _home - _particle[p].position;
|
glm::vec3 vectorToHome = myEmitter.position - _particle[p].position;
|
||||||
_particle[p].velocity += vectorToHome * _homeAttraction * deltaTime;
|
_particle[p].velocity += vectorToHome * myEmitter.particleAttributes.emitterAttraction * deltaTime;
|
||||||
|
|
||||||
// apply neighbor attraction
|
// apply neighbor attraction
|
||||||
int neighbor = p + 1;
|
int neighbor = p + 1;
|
||||||
if (neighbor == _numberOfParticles ) {
|
if (neighbor == _numParticles ) {
|
||||||
neighbor = 0;
|
neighbor = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( _particle[neighbor].emitterIndex == _particle[p].emitterIndex) {
|
||||||
glm::vec3 vectorToNeighbor = _particle[p].position - _particle[neighbor].position;
|
glm::vec3 vectorToNeighbor = _particle[p].position - _particle[neighbor].position;
|
||||||
|
|
||||||
_particle[p].velocity -= vectorToNeighbor * _neighborAttraction * deltaTime;
|
_particle[p].velocity -= vectorToNeighbor * myEmitter.particleAttributes.neighborAttraction * deltaTime;
|
||||||
|
|
||||||
float distanceToNeighbor = glm::length(vectorToNeighbor);
|
float distanceToNeighbor = glm::length(vectorToNeighbor);
|
||||||
if (distanceToNeighbor > 0.0f) {
|
if (distanceToNeighbor > 0.0f) {
|
||||||
_particle[neighbor].velocity += (vectorToNeighbor / ( 1.0f + distanceToNeighbor * distanceToNeighbor)) * _neighborRepulsion * deltaTime;
|
_particle[neighbor].velocity += (vectorToNeighbor / ( 1.0f + distanceToNeighbor * distanceToNeighbor)) * myEmitter.particleAttributes.neighborRepulsion * deltaTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply tornado force
|
// apply tornado force
|
||||||
glm::vec3 tornadoDirection = glm::cross(vectorToHome, _tornadoAxis);
|
glm::vec3 tornadoDirection = glm::cross(vectorToHome, myEmitter.up);
|
||||||
_particle[p].velocity += tornadoDirection * _tornadoForce * deltaTime;
|
_particle[p].velocity += tornadoDirection * myEmitter.particleAttributes.tornadoForce * deltaTime;
|
||||||
|
|
||||||
// apply air friction
|
// apply air friction
|
||||||
float drag = 1.0 - _airFriction * deltaTime;
|
float drag = 1.0 - myEmitter.particleAttributes.airFriction * deltaTime;
|
||||||
if (drag < 0.0f) {
|
if (drag < 0.0f) {
|
||||||
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
|
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||||
} else {
|
} else {
|
||||||
|
@ -125,7 +216,7 @@ void ParticleSystem::updateParticle(int p, float deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply gravity
|
// apply gravity
|
||||||
_particle[p].velocity.y -= _gravity * deltaTime;
|
_particle[p].velocity -= _upDirection * myEmitter.particleAttributes.gravity * deltaTime;
|
||||||
|
|
||||||
// update position by velocity
|
// update position by velocity
|
||||||
_particle[p].position += _particle[p].velocity;
|
_particle[p].position += _particle[p].velocity;
|
||||||
|
@ -135,28 +226,51 @@ void ParticleSystem::updateParticle(int p, float deltaTime) {
|
||||||
_particle[p].position.y = _particle[p].radius;
|
_particle[p].position.y = _particle[p].radius;
|
||||||
|
|
||||||
if (_particle[p].velocity.y < 0.0f) {
|
if (_particle[p].velocity.y < 0.0f) {
|
||||||
_particle[p].velocity.y *= -_bounce;
|
_particle[p].velocity.y *= -myEmitter.particleAttributes.bounce;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// collision with sphere
|
// collision with sphere
|
||||||
glm::vec3 vectorToSphereCenter = _TEST_bigSpherePosition - _particle[p].position;
|
if (myEmitter.particleAttributes.usingCollisionSphere) {
|
||||||
|
glm::vec3 vectorToSphereCenter = myEmitter.particleAttributes.collisionSpherePosition - _particle[p].position;
|
||||||
float distanceToSphereCenter = glm::length(vectorToSphereCenter);
|
float distanceToSphereCenter = glm::length(vectorToSphereCenter);
|
||||||
float combinedRadius = _TEST_bigSphereRadius + _particle[p].radius;
|
float combinedRadius = myEmitter.particleAttributes.collisionSphereRadius + _particle[p].radius;
|
||||||
if (distanceToSphereCenter < combinedRadius) {
|
if (distanceToSphereCenter < combinedRadius) {
|
||||||
|
|
||||||
if (distanceToSphereCenter > 0.0f){
|
if (distanceToSphereCenter > 0.0f){
|
||||||
glm::vec3 directionToSphereCenter = vectorToSphereCenter / distanceToSphereCenter;
|
glm::vec3 directionToSphereCenter = vectorToSphereCenter / distanceToSphereCenter;
|
||||||
_particle[p].position = _TEST_bigSpherePosition - directionToSphereCenter * combinedRadius;
|
_particle[p].position = myEmitter.particleAttributes.collisionSpherePosition - directionToSphereCenter * combinedRadius;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setCollisionSphere(int e, glm::vec3 position, float radius) {
|
||||||
|
_emitter[e].particleAttributes.usingCollisionSphere = true;
|
||||||
|
_emitter[e].particleAttributes.collisionSpherePosition = position;
|
||||||
|
_emitter[e].particleAttributes.collisionSphereRadius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
void ParticleSystem::render() {
|
void ParticleSystem::render() {
|
||||||
|
|
||||||
for (unsigned int p = 0; p < _numberOfParticles; p++) {
|
// render the emitters
|
||||||
glColor3f(_particle[p].color.x, _particle[p].color.y, _particle[p].color.z);
|
for (unsigned int e = 0; e < _numEmitters; e++) {
|
||||||
|
if (_emitter[e].showingEmitter) {
|
||||||
|
renderEmitter(e, 0.2f);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// render the particles
|
||||||
|
for (unsigned int p = 0; p < _numParticles; p++) {
|
||||||
|
if (_particle[p].alive) {
|
||||||
|
renderParticle(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::renderParticle(int p) {
|
||||||
|
|
||||||
|
glColor4f(_particle[p].color.r, _particle[p].color.g, _particle[p].color.b, _particle[p].color.a );
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glTranslatef(_particle[p].position.x, _particle[p].position.y, _particle[p].position.z);
|
glTranslatef(_particle[p].position.x, _particle[p].position.y, _particle[p].position.z);
|
||||||
glutSolidSphere(_particle[p].radius, 6, 6);
|
glutSolidSphere(_particle[p].radius, 6, 6);
|
||||||
|
@ -170,8 +284,35 @@ void ParticleSystem::render() {
|
||||||
glVertex3f(end.x, end.y, end.z);
|
glVertex3f(end.x, end.y, end.z);
|
||||||
|
|
||||||
glEnd();
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
void ParticleSystem::renderEmitter(int e, float size) {
|
||||||
|
|
||||||
|
glm::vec3 r = _emitter[e].right * size;
|
||||||
|
glm::vec3 u = _emitter[e].up * size;
|
||||||
|
glm::vec3 f = _emitter[e].front * size;
|
||||||
|
|
||||||
|
glLineWidth(2.0f);
|
||||||
|
|
||||||
|
glColor3f(0.8f, 0.4, 0.4);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glVertex3f(_emitter[e].position.x, _emitter[e].position.y, _emitter[e].position.z);
|
||||||
|
glVertex3f(_emitter[e].position.x + r.x, _emitter[e].position.y + r.y, _emitter[e].position.z + r.z);
|
||||||
|
glEnd();
|
||||||
|
|
||||||
|
glColor3f(0.4f, 0.8, 0.4);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glVertex3f(_emitter[e].position.x, _emitter[e].position.y, _emitter[e].position.z);
|
||||||
|
glVertex3f(_emitter[e].position.x + u.x, _emitter[e].position.y + u.y, _emitter[e].position.z + u.z);
|
||||||
|
glEnd();
|
||||||
|
|
||||||
|
glColor3f(0.4f, 0.4, 0.8);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glVertex3f(_emitter[e].position.x, _emitter[e].position.y, _emitter[e].position.z);
|
||||||
|
glVertex3f(_emitter[e].position.x + f.x, _emitter[e].position.y + f.y, _emitter[e].position.z + f.z);
|
||||||
|
glEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -180,4 +321,3 @@ void ParticleSystem::render() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,51 +9,82 @@
|
||||||
#ifndef hifi_ParticleSystem_h
|
#ifndef hifi_ParticleSystem_h
|
||||||
#define hifi_ParticleSystem_h
|
#define hifi_ParticleSystem_h
|
||||||
|
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
const int MAX_PARTICLES = 5000;
|
const int MAX_PARTICLES = 5000;
|
||||||
const int MAX_EMITTERS = 10;
|
const int MAX_EMITTERS = 20;
|
||||||
|
|
||||||
class ParticleSystem {
|
class ParticleSystem {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
struct ParticleAttributes {
|
||||||
|
float bounce;
|
||||||
|
float gravity;
|
||||||
|
float airFriction;
|
||||||
|
float jitter;
|
||||||
|
float emitterAttraction;
|
||||||
|
float tornadoForce;
|
||||||
|
float neighborAttraction;
|
||||||
|
float neighborRepulsion;
|
||||||
|
bool usingCollisionSphere;
|
||||||
|
glm::vec3 collisionSpherePosition;
|
||||||
|
float collisionSphereRadius;
|
||||||
|
};
|
||||||
|
|
||||||
ParticleSystem();
|
ParticleSystem();
|
||||||
|
|
||||||
|
int addEmitter(); // add (create) an emitter and get its unique id
|
||||||
|
void emitParticlesNow(int e, int numParticles, float radius, glm::vec4 color, glm::vec3 velocity, float lifespan);
|
||||||
void simulate(float deltaTime);
|
void simulate(float deltaTime);
|
||||||
void render();
|
void render();
|
||||||
|
|
||||||
private:
|
void setParticleAttributesForEmitter(int emitterIndex, ParticleAttributes attributes);
|
||||||
|
void setOrangeBlueColorPalette(); // apply a nice preset color palette to the particles
|
||||||
|
void setUpDirection(glm::vec3 upDirection) {_upDirection = upDirection;} // tell particle system which direction is up
|
||||||
|
|
||||||
struct Particle {
|
void setCollisionSphere(int emitterIndex, glm::vec3 position, float radius); // specify a sphere for the particles to collide with
|
||||||
glm::vec3 position;
|
void setEmitterPosition(int emitterIndex, glm::vec3 position) { _emitter[emitterIndex].position = position; } // set position of emitter
|
||||||
glm::vec3 velocity;
|
void setEmitterRotation(int emitterIndex, glm::quat rotation) { _emitter[emitterIndex].rotation = rotation; } // set rotation of emitter
|
||||||
glm::vec3 color;
|
void setShowingEmitter (int emitterIndex, bool showing ) { _emitter[emitterIndex].showingEmitter = showing; } // set its visibiity
|
||||||
float age;
|
|
||||||
float radius;
|
private:
|
||||||
};
|
|
||||||
|
|
||||||
struct Emitter {
|
struct Emitter {
|
||||||
glm::vec3 position;
|
glm::vec3 position;
|
||||||
glm::vec3 direction;
|
glm::quat rotation;
|
||||||
|
glm::vec3 right;
|
||||||
|
glm::vec3 up;
|
||||||
|
glm::vec3 front;
|
||||||
|
bool showingEmitter;
|
||||||
|
ParticleAttributes particleAttributes;
|
||||||
};
|
};
|
||||||
|
|
||||||
float _bounce;
|
struct Particle {
|
||||||
float _gravity;
|
bool alive; // is the particle active?
|
||||||
|
glm::vec3 position; // position
|
||||||
|
glm::vec3 velocity; // velocity
|
||||||
|
glm::vec4 color; // color (rgba)
|
||||||
|
float age; // age in seconds
|
||||||
|
float radius; // radius
|
||||||
|
float lifespan; // how long this particle stays alive (in seconds)
|
||||||
|
int emitterIndex; // which emitter created this particle?
|
||||||
|
};
|
||||||
|
|
||||||
|
glm::vec3 _upDirection;
|
||||||
float _timer;
|
float _timer;
|
||||||
Emitter _emitter[MAX_EMITTERS];
|
Emitter _emitter[MAX_EMITTERS];
|
||||||
Particle _particle[MAX_PARTICLES];
|
Particle _particle[MAX_PARTICLES];
|
||||||
int _numberOfParticles;
|
int _numParticles;
|
||||||
glm::vec3 _home;
|
int _numEmitters;
|
||||||
glm::vec3 _tornadoAxis;
|
|
||||||
float _airFriction;
|
|
||||||
float _jitter;
|
|
||||||
float _homeAttraction;
|
|
||||||
float _tornadoForce;
|
|
||||||
float _neighborAttraction;
|
|
||||||
float _neighborRepulsion;
|
|
||||||
float _TEST_bigSphereRadius;
|
|
||||||
glm::vec3 _TEST_bigSpherePosition;
|
|
||||||
|
|
||||||
// private methods
|
// private methods
|
||||||
|
void updateEmitter(int e, float deltaTime);
|
||||||
void updateParticle(int index, float deltaTime);
|
void updateParticle(int index, float deltaTime);
|
||||||
void runSpecialEffectsTest(float deltaTime);
|
void createParticle(int e, glm::vec3 position, glm::vec3 velocity, float radius, glm::vec4 color, float lifespan);
|
||||||
|
//void runSpecialEffectsTest(int e, float deltaTime); // for debugging and artistic exploration
|
||||||
|
void killParticle(int p);
|
||||||
|
void renderEmitter(int emitterIndex, float size);
|
||||||
|
void renderParticle(int p);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -325,22 +325,38 @@ void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, gl
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void renderCollisionOverlay(int width, int height, float magnitude) {
|
||||||
|
const float MIN_VISIBLE_COLLISION = 0.01f;
|
||||||
|
if (magnitude > MIN_VISIBLE_COLLISION) {
|
||||||
|
glColor4f(0, 0, 0, magnitude);
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
glVertex2f(0, 0);
|
||||||
|
glVertex2d(width, 0);
|
||||||
|
glVertex2d(width, height);
|
||||||
|
glVertex2d(0, height);
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void drawGroundPlaneGrid(float size) {
|
void renderGroundPlaneGrid(float size, float impact) {
|
||||||
glColor3f(0.4f, 0.5f, 0.3f);
|
|
||||||
glLineWidth(2.0);
|
glLineWidth(2.0);
|
||||||
|
glm::vec4 impactColor(1, 0, 0, 1);
|
||||||
|
glm::vec3 lineColor(0.4, 0.5, 0.3);
|
||||||
|
glm::vec4 surfaceColor(0.5, 0.5, 0.5, 0.4);
|
||||||
|
|
||||||
|
glColor3fv(&lineColor.x);
|
||||||
for (float x = 0; x <= size; x++) {
|
for (float x = 0; x <= size; x++) {
|
||||||
glBegin(GL_LINES);
|
glBegin(GL_LINES);
|
||||||
glVertex3f(x, 0.0f, 0);
|
glVertex3f(x, 0, 0);
|
||||||
glVertex3f(x, 0.0f, size);
|
glVertex3f(x, 0, size);
|
||||||
glVertex3f(0, 0.0f, x);
|
glVertex3f(0, 0, x);
|
||||||
glVertex3f(size, 0.0f, x);
|
glVertex3f(size, 0, x);
|
||||||
glEnd();
|
glEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw a translucent quad just underneath the grid.
|
// Draw the floor, colored for recent impact
|
||||||
glColor4f(0.5, 0.5, 0.5, 0.4);
|
glm::vec4 floorColor = impact * impactColor + (1.f - impact) * surfaceColor;
|
||||||
|
glColor4fv(&floorColor.x);
|
||||||
glBegin(GL_QUADS);
|
glBegin(GL_QUADS);
|
||||||
glVertex3f(0, 0, 0);
|
glVertex3f(0, 0, 0);
|
||||||
glVertex3f(size, 0, 0);
|
glVertex3f(size, 0, 0);
|
||||||
|
|
|
@ -57,7 +57,10 @@ glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
|
||||||
|
|
||||||
double diffclock(timeval *clock1,timeval *clock2);
|
double diffclock(timeval *clock1,timeval *clock2);
|
||||||
|
|
||||||
void drawGroundPlaneGrid(float size);
|
void renderGroundPlaneGrid(float size, float impact);
|
||||||
|
|
||||||
|
void renderCollisionOverlay(int width, int height, float magnitude);
|
||||||
|
|
||||||
|
|
||||||
void renderDiskShadow(glm::vec3 position, glm::vec3 upDirection, float radius, float darkness);
|
void renderDiskShadow(glm::vec3 position, glm::vec3 upDirection, float radius, float darkness);
|
||||||
|
|
||||||
|
|
|
@ -430,4 +430,3 @@ int unpackFloatFromByte(unsigned char* buffer, float& value, float scaleBy) {
|
||||||
value = ((float)holder / (float) 255) * scaleBy;
|
value = ((float)holder / (float) 255) * scaleBy;
|
||||||
return sizeof(holder);
|
return sizeof(holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ const int WANT_OCCLUSION_CULLING_BIT = 7; // 8th bit
|
||||||
|
|
||||||
const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation
|
const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation
|
||||||
|
|
||||||
|
|
||||||
enum KeyState
|
enum KeyState
|
||||||
{
|
{
|
||||||
NO_KEY_DOWN = 0,
|
NO_KEY_DOWN = 0,
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <algorithm> // std:min
|
#include <algorithm> // std:min
|
||||||
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
#include "OctalCode.h"
|
#include "OctalCode.h"
|
||||||
|
|
||||||
int numberOfThreeBitSectionsInCode(unsigned char * octalCode) {
|
int numberOfThreeBitSectionsInCode(unsigned char * octalCode) {
|
||||||
|
assert(octalCode);
|
||||||
if (*octalCode == 255) {
|
if (*octalCode == 255) {
|
||||||
return *octalCode + numberOfThreeBitSectionsInCode(octalCode + 1);
|
return *octalCode + numberOfThreeBitSectionsInCode(octalCode + 1);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -21,6 +21,7 @@ const glm::vec3 IDENTITY_UP = glm::vec3( 0.0f, 1.0f, 0.0f);
|
||||||
const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f);
|
const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f);
|
||||||
|
|
||||||
const bool LOW_RES_MONO = false; // while in "low res mode" do voxels switch to monochrome
|
const bool LOW_RES_MONO = false; // while in "low res mode" do voxels switch to monochrome
|
||||||
|
const uint64_t CHANGE_FUDGE = 1000 * 200; // useconds of fudge in determining if we want to resend changed voxels
|
||||||
|
|
||||||
const int TREE_SCALE = 128;
|
const int TREE_SCALE = 128;
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,8 @@ void VoxelNode::init(unsigned char * octalCode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
VoxelNode::~VoxelNode() {
|
VoxelNode::~VoxelNode() {
|
||||||
|
notifyDeleteHooks();
|
||||||
|
|
||||||
delete[] _octalCode;
|
delete[] _octalCode;
|
||||||
|
|
||||||
// delete all of this node's children
|
// delete all of this node's children
|
||||||
|
@ -387,3 +389,44 @@ float VoxelNode::distanceToPoint(const glm::vec3& point) const {
|
||||||
float distance = sqrtf(glm::dot(temp, temp));
|
float distance = sqrtf(glm::dot(temp, temp));
|
||||||
return distance;
|
return distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VoxelNodeDeleteHook VoxelNode::_hooks[VOXEL_NODE_MAX_DELETE_HOOKS];
|
||||||
|
void* VoxelNode::_hooksExtraData[VOXEL_NODE_MAX_DELETE_HOOKS];
|
||||||
|
int VoxelNode::_hooksInUse = 0;
|
||||||
|
|
||||||
|
int VoxelNode::addDeleteHook(VoxelNodeDeleteHook hook, void* extraData) {
|
||||||
|
// If first use, initialize the _hooks array
|
||||||
|
if (_hooksInUse == 0) {
|
||||||
|
memset(_hooks, 0, sizeof(_hooks));
|
||||||
|
memset(_hooksExtraData, 0, sizeof(_hooksExtraData));
|
||||||
|
}
|
||||||
|
// find first available slot
|
||||||
|
for (int i = 0; i < VOXEL_NODE_MAX_DELETE_HOOKS; i++) {
|
||||||
|
if (!_hooks[i]) {
|
||||||
|
_hooks[i] = hook;
|
||||||
|
_hooksExtraData[i] = extraData;
|
||||||
|
_hooksInUse++;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we got here, then we're out of room in our hooks, return error
|
||||||
|
return VOXEL_NODE_NO_MORE_HOOKS_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelNode::removeDeleteHook(int hookID) {
|
||||||
|
if (_hooks[hookID]) {
|
||||||
|
_hooks[hookID] = NULL;
|
||||||
|
_hooksExtraData[hookID] = NULL;
|
||||||
|
_hooksInUse--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelNode::notifyDeleteHooks() {
|
||||||
|
if (_hooksInUse > 0) {
|
||||||
|
for (int i = 0; i < VOXEL_NODE_MAX_DELETE_HOOKS; i++) {
|
||||||
|
if (_hooks[i]) {
|
||||||
|
_hooks[i](this, _hooksExtraData[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,33 +15,19 @@
|
||||||
#include "VoxelConstants.h"
|
#include "VoxelConstants.h"
|
||||||
|
|
||||||
class VoxelTree; // forward delclaration
|
class VoxelTree; // forward delclaration
|
||||||
|
class VoxelNode; // forward delclaration
|
||||||
|
|
||||||
typedef unsigned char colorPart;
|
typedef unsigned char colorPart;
|
||||||
typedef unsigned char nodeColor[4];
|
typedef unsigned char nodeColor[4];
|
||||||
typedef unsigned char rgbColor[3];
|
typedef unsigned char rgbColor[3];
|
||||||
|
|
||||||
|
// Callback function, for delete hook
|
||||||
|
typedef void (*VoxelNodeDeleteHook)(VoxelNode* node, void* extraData);
|
||||||
|
const int VOXEL_NODE_MAX_DELETE_HOOKS = 100;
|
||||||
|
const int VOXEL_NODE_NO_MORE_HOOKS_AVAILABLE = -1;
|
||||||
|
|
||||||
|
|
||||||
class VoxelNode {
|
class VoxelNode {
|
||||||
private:
|
|
||||||
nodeColor _trueColor;
|
|
||||||
#ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color
|
|
||||||
nodeColor _currentColor;
|
|
||||||
bool _falseColored;
|
|
||||||
#endif
|
|
||||||
glBufferIndex _glBufferIndex;
|
|
||||||
bool _isDirty;
|
|
||||||
uint64_t _lastChanged;
|
|
||||||
bool _shouldRender;
|
|
||||||
bool _isStagedForDeletion;
|
|
||||||
AABox _box;
|
|
||||||
unsigned char* _octalCode;
|
|
||||||
VoxelNode* _children[8];
|
|
||||||
int _childCount;
|
|
||||||
float _density; // If leaf: density = 1, if internal node: 0-1 density of voxels inside
|
|
||||||
|
|
||||||
void calculateAABox();
|
|
||||||
|
|
||||||
void init(unsigned char * octalCode);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VoxelNode(); // root node constructor
|
VoxelNode(); // root node constructor
|
||||||
VoxelNode(unsigned char * octalCode); // regular constructor
|
VoxelNode(unsigned char * octalCode); // regular constructor
|
||||||
|
@ -85,6 +71,7 @@ public:
|
||||||
void clearDirtyBit() { _isDirty = false; };
|
void clearDirtyBit() { _isDirty = false; };
|
||||||
bool hasChangedSince(uint64_t time) const { return (_lastChanged > time); };
|
bool hasChangedSince(uint64_t time) const { return (_lastChanged > time); };
|
||||||
void markWithChangedTime() { _lastChanged = usecTimestampNow(); };
|
void markWithChangedTime() { _lastChanged = usecTimestampNow(); };
|
||||||
|
uint64_t getLastChanged() const { return _lastChanged; };
|
||||||
void handleSubtreeChanged(VoxelTree* myTree);
|
void handleSubtreeChanged(VoxelTree* myTree);
|
||||||
|
|
||||||
glBufferIndex getBufferIndex() const { return _glBufferIndex; };
|
glBufferIndex getBufferIndex() const { return _glBufferIndex; };
|
||||||
|
@ -117,6 +104,33 @@ public:
|
||||||
const nodeColor& getTrueColor() const { return _trueColor; };
|
const nodeColor& getTrueColor() const { return _trueColor; };
|
||||||
const nodeColor& getColor() const { return _trueColor; };
|
const nodeColor& getColor() const { return _trueColor; };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int addDeleteHook(VoxelNodeDeleteHook hook, void* extraData = NULL);
|
||||||
|
static void removeDeleteHook(int hookID);
|
||||||
|
private:
|
||||||
|
void calculateAABox();
|
||||||
|
void init(unsigned char * octalCode);
|
||||||
|
void notifyDeleteHooks();
|
||||||
|
|
||||||
|
nodeColor _trueColor;
|
||||||
|
#ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color
|
||||||
|
nodeColor _currentColor;
|
||||||
|
bool _falseColored;
|
||||||
|
#endif
|
||||||
|
glBufferIndex _glBufferIndex;
|
||||||
|
bool _isDirty;
|
||||||
|
uint64_t _lastChanged;
|
||||||
|
bool _shouldRender;
|
||||||
|
bool _isStagedForDeletion;
|
||||||
|
AABox _box;
|
||||||
|
unsigned char* _octalCode;
|
||||||
|
VoxelNode* _children[8];
|
||||||
|
int _childCount;
|
||||||
|
float _density; // If leaf: density = 1, if internal node: 0-1 density of voxels inside
|
||||||
|
|
||||||
|
static VoxelNodeDeleteHook _hooks[VOXEL_NODE_MAX_DELETE_HOOKS];
|
||||||
|
static void* _hooksExtraData[VOXEL_NODE_MAX_DELETE_HOOKS];
|
||||||
|
static int _hooksInUse;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__VoxelNode__) */
|
#endif /* defined(__hifi__VoxelNode__) */
|
||||||
|
|
|
@ -9,7 +9,15 @@
|
||||||
#include "VoxelNodeBag.h"
|
#include "VoxelNodeBag.h"
|
||||||
#include <OctalCode.h>
|
#include <OctalCode.h>
|
||||||
|
|
||||||
|
VoxelNodeBag::VoxelNodeBag() :
|
||||||
|
_bagElements(NULL),
|
||||||
|
_elementsInUse(0),
|
||||||
|
_sizeOfElementsArray(0) {
|
||||||
|
_hookID = VoxelNode::addDeleteHook(voxelNodeDeleteHook, (void*)this);
|
||||||
|
};
|
||||||
|
|
||||||
VoxelNodeBag::~VoxelNodeBag() {
|
VoxelNodeBag::~VoxelNodeBag() {
|
||||||
|
VoxelNode::removeDeleteHook(_hookID);
|
||||||
deleteAll();
|
deleteAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,3 +126,9 @@ void VoxelNodeBag::remove(VoxelNode* node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VoxelNodeBag::voxelNodeDeleteHook(VoxelNode* node, void* extraData) {
|
||||||
|
VoxelNodeBag* theBag = (VoxelNodeBag*)extraData;
|
||||||
|
theBag->remove(node); // note: remove can safely handle nodes that aren't in it, so we don't need to check contains()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,7 @@
|
||||||
class VoxelNodeBag {
|
class VoxelNodeBag {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VoxelNodeBag() :
|
VoxelNodeBag();
|
||||||
_bagElements(NULL),
|
|
||||||
_elementsInUse(0),
|
|
||||||
_sizeOfElementsArray(0) {};
|
|
||||||
|
|
||||||
~VoxelNodeBag();
|
~VoxelNodeBag();
|
||||||
|
|
||||||
void insert(VoxelNode* node); // put a node into the bag
|
void insert(VoxelNode* node); // put a node into the bag
|
||||||
|
@ -36,11 +32,14 @@ public:
|
||||||
|
|
||||||
void deleteAll();
|
void deleteAll();
|
||||||
|
|
||||||
|
static void voxelNodeDeleteHook(VoxelNode* node, void* extraData);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
VoxelNode** _bagElements;
|
VoxelNode** _bagElements;
|
||||||
int _elementsInUse;
|
int _elementsInUse;
|
||||||
int _sizeOfElementsArray;
|
int _sizeOfElementsArray;
|
||||||
|
int _hookID;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__VoxelNodeBag__) */
|
#endif /* defined(__hifi__VoxelNodeBag__) */
|
||||||
|
|
|
@ -373,7 +373,6 @@ void VoxelTree::deleteVoxelAt(float x, float y, float z, float s, bool stage) {
|
||||||
delete[] octalCode; // cleanup memory
|
delete[] octalCode; // cleanup memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class DeleteVoxelCodeFromTreeArgs {
|
class DeleteVoxelCodeFromTreeArgs {
|
||||||
public:
|
public:
|
||||||
bool stage;
|
bool stage;
|
||||||
|
@ -1005,6 +1004,7 @@ bool VoxelTree::findCapsulePenetration(const glm::vec3& start, const glm::vec3&
|
||||||
return args.found;
|
return args.found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
||||||
EncodeBitstreamParams& params) const {
|
EncodeBitstreamParams& params) const {
|
||||||
|
|
||||||
|
@ -1107,11 +1107,22 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we were in view, then bail out early!
|
// If we were previously in the view, then we normally will return out of here and stop recursing. But
|
||||||
if (wasInView) {
|
// if we're in deltaViewFrustum mode, and this node has changed since it was last sent, then we do
|
||||||
|
// need to send it.
|
||||||
|
if (wasInView && !(params.deltaViewFrustum && node->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE))) {
|
||||||
return bytesAtThisLevel;
|
return bytesAtThisLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Not ready for production - coming soon.
|
||||||
|
// If we're not in delta sending mode, but the voxel hasn't changed, then we can also bail early...
|
||||||
|
if (!params.deltaViewFrustum && !node->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE)) {
|
||||||
|
printf("not delta sending, and the node hasn't changed, bail early... lastSent=%lld getLastChanged=%lld\n",
|
||||||
|
params.lastViewFrustumSent, node->getLastChanged());
|
||||||
|
return bytesAtThisLevel;
|
||||||
|
}
|
||||||
|
**/
|
||||||
|
|
||||||
// If the user also asked for occlusion culling, check if this node is occluded, but only if it's not a leaf.
|
// If the user also asked for occlusion culling, check if this node is occluded, but only if it's not a leaf.
|
||||||
// leaf occlusion is handled down below when we check child nodes
|
// leaf occlusion is handled down below when we check child nodes
|
||||||
if (params.wantOcclusionCulling && !node->isLeaf()) {
|
if (params.wantOcclusionCulling && !node->isLeaf()) {
|
||||||
|
@ -1270,8 +1281,12 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If our child wasn't in view (or we're ignoring wasInView) then we add it to our sending items
|
// If our child wasn't in view (or we're ignoring wasInView) then we add it to our sending items.
|
||||||
if (!childWasInView) {
|
// Or if we were previously in the view, but this node has changed since it was last sent, then we do
|
||||||
|
// need to send it.
|
||||||
|
if (!childWasInView ||
|
||||||
|
(params.deltaViewFrustum &&
|
||||||
|
childNode->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE))){
|
||||||
childrenColoredBits += (1 << (7 - originalIndex));
|
childrenColoredBits += (1 << (7 - originalIndex));
|
||||||
inViewWithColorCount++;
|
inViewWithColorCount++;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1355,7 +1370,6 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
||||||
if (oneAtBit(childrenExistInPacketBits, originalIndex)) {
|
if (oneAtBit(childrenExistInPacketBits, originalIndex)) {
|
||||||
|
|
||||||
int thisLevel = currentEncodeLevel;
|
int thisLevel = currentEncodeLevel;
|
||||||
|
|
||||||
// remember this for reshuffling
|
// remember this for reshuffling
|
||||||
recursiveSliceStarts[originalIndex] = outputBuffer;
|
recursiveSliceStarts[originalIndex] = outputBuffer;
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
|
||||||
#define DONT_CHOP 0
|
#define DONT_CHOP 0
|
||||||
#define NO_BOUNDARY_ADJUST 0
|
#define NO_BOUNDARY_ADJUST 0
|
||||||
#define LOW_RES_MOVING_ADJUST 1
|
#define LOW_RES_MOVING_ADJUST 1
|
||||||
|
#define IGNORE_LAST_SENT 0
|
||||||
|
|
||||||
class EncodeBitstreamParams {
|
class EncodeBitstreamParams {
|
||||||
public:
|
public:
|
||||||
|
@ -49,6 +50,7 @@ public:
|
||||||
bool wantOcclusionCulling;
|
bool wantOcclusionCulling;
|
||||||
long childWasInViewDiscarded;
|
long childWasInViewDiscarded;
|
||||||
int boundaryLevelAdjust;
|
int boundaryLevelAdjust;
|
||||||
|
uint64_t lastViewFrustumSent;
|
||||||
|
|
||||||
CoverageMap* map;
|
CoverageMap* map;
|
||||||
|
|
||||||
|
@ -62,7 +64,8 @@ public:
|
||||||
const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM,
|
const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM,
|
||||||
bool wantOcclusionCulling= NO_OCCLUSION_CULLING,
|
bool wantOcclusionCulling= NO_OCCLUSION_CULLING,
|
||||||
CoverageMap* map = IGNORE_COVERAGE_MAP,
|
CoverageMap* map = IGNORE_COVERAGE_MAP,
|
||||||
int boundaryLevelAdjust = NO_BOUNDARY_ADJUST) :
|
int boundaryLevelAdjust = NO_BOUNDARY_ADJUST,
|
||||||
|
uint64_t lastViewFrustumSent = IGNORE_LAST_SENT) :
|
||||||
maxEncodeLevel (maxEncodeLevel),
|
maxEncodeLevel (maxEncodeLevel),
|
||||||
maxLevelReached (0),
|
maxLevelReached (0),
|
||||||
viewFrustum (viewFrustum),
|
viewFrustum (viewFrustum),
|
||||||
|
@ -74,6 +77,7 @@ public:
|
||||||
wantOcclusionCulling (wantOcclusionCulling),
|
wantOcclusionCulling (wantOcclusionCulling),
|
||||||
childWasInViewDiscarded (0),
|
childWasInViewDiscarded (0),
|
||||||
boundaryLevelAdjust (boundaryLevelAdjust),
|
boundaryLevelAdjust (boundaryLevelAdjust),
|
||||||
|
lastViewFrustumSent (lastViewFrustumSent),
|
||||||
map (map)
|
map (map)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "PacketHeaders.h"
|
#include "PacketHeaders.h"
|
||||||
|
#include "SharedUtil.h"
|
||||||
#include "VoxelNodeData.h"
|
#include "VoxelNodeData.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
@ -23,7 +24,6 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) :
|
||||||
{
|
{
|
||||||
_voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE];
|
_voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE];
|
||||||
_voxelPacketAt = _voxelPacket;
|
_voxelPacketAt = _voxelPacket;
|
||||||
|
|
||||||
resetVoxelPacket();
|
resetVoxelPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,5 +80,9 @@ void VoxelNodeData::updateLastKnownViewFrustum() {
|
||||||
// save our currentViewFrustum into our lastKnownViewFrustum
|
// save our currentViewFrustum into our lastKnownViewFrustum
|
||||||
_lastKnownViewFrustum = _currentViewFrustum;
|
_lastKnownViewFrustum = _currentViewFrustum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save that we know the view has been sent.
|
||||||
|
uint64_t now = usecTimestampNow();
|
||||||
|
setLastTimeBagEmpty(now); // is this what we want? poor names
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -173,10 +173,10 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
||||||
// If the current view frustum has changed OR we have nothing to send, then search against
|
// If the current view frustum has changed OR we have nothing to send, then search against
|
||||||
// the current view frustum for things to send.
|
// the current view frustum for things to send.
|
||||||
if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) {
|
if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) {
|
||||||
|
uint64_t now = usecTimestampNow();
|
||||||
if (::debugVoxelSending) {
|
if (::debugVoxelSending) {
|
||||||
printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n",
|
printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n",
|
||||||
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()));
|
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()));
|
||||||
uint64_t now = usecTimestampNow();
|
|
||||||
if (nodeData->getLastTimeBagEmpty() > 0) {
|
if (nodeData->getLastTimeBagEmpty() > 0) {
|
||||||
float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f;
|
float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f;
|
||||||
if (viewFrustumChanged) {
|
if (viewFrustumChanged) {
|
||||||
|
@ -188,7 +188,6 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
||||||
debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta),
|
debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta),
|
||||||
debug::valueOf(wantColor));
|
debug::valueOf(wantColor));
|
||||||
}
|
}
|
||||||
nodeData->setLastTimeBagEmpty(now); // huh? why is this inside debug? probably not what we want
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if our view has changed, we need to reset these things...
|
// if our view has changed, we need to reset these things...
|
||||||
|
@ -197,6 +196,15 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
||||||
nodeData->map.erase();
|
nodeData->map.erase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!viewFrustumChanged && !nodeData->getWantDelta()) {
|
||||||
|
// only set our last sent time if we weren't resetting due to frustum change
|
||||||
|
uint64_t now = usecTimestampNow();
|
||||||
|
nodeData->setLastTimeBagEmpty(now);
|
||||||
|
if (::debugVoxelSending) {
|
||||||
|
printf("ENTIRE SCENE SENT! nodeData->setLastTimeBagEmpty(now=[%lld])\n", now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nodeData->nodeBag.insert(serverTree.rootNode);
|
nodeData->nodeBag.insert(serverTree.rootNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +241,8 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
||||||
|
|
||||||
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor,
|
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor,
|
||||||
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
|
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
|
||||||
wantOcclusionCulling, coverageMap, boundaryLevelAdjust);
|
wantOcclusionCulling, coverageMap, boundaryLevelAdjust,
|
||||||
|
nodeData->getLastTimeBagEmpty());
|
||||||
|
|
||||||
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
|
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
|
||||||
nodeData->nodeBag, params);
|
nodeData->nodeBag, params);
|
||||||
|
@ -375,7 +384,6 @@ void attachVoxelNodeDataToNode(Node* newNode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char * argv[]) {
|
int main(int argc, const char * argv[]) {
|
||||||
|
|
||||||
pthread_mutex_init(&::treeLock, NULL);
|
pthread_mutex_init(&::treeLock, NULL);
|
||||||
|
|
||||||
qInstallMsgHandler(sharedMessageHandler);
|
qInstallMsgHandler(sharedMessageHandler);
|
||||||
|
|
Loading…
Reference in a new issue