diff --git a/scripts/apothecary b/scripts/apothecary deleted file mode 160000 index d05cfc80571..00000000000 --- a/scripts/apothecary +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d05cfc8057131aaa8289b9a003be74c9c2c1f985 diff --git a/workshops/NoiseWorkshop/EmptyGPUParticles/src/Math/MathUtils.h b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Math/MathUtils.h new file mode 100644 index 00000000000..e3af4847937 --- /dev/null +++ b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Math/MathUtils.h @@ -0,0 +1,242 @@ +#pragma once + +#include "ofMain.h" + +class MathUtils +{ + + public: + + // ------------------------------------------------------------ + // Step functions + // ------------------------------------------------------------ + + // ------------------------------------------------------------ + static float step(float a, float x) + { + return (float) (x >= a); + } + + // ------------------------------------------------------------ + static float linearStep( float _edge0, float _edge1, float _t ) + { + // Scale, and clamp x to 0..1 range + return ofClamp( (_t - _edge0)/(_edge1 - _edge0), 0.0f, 1.0f); + } + + // ------------------------------------------------------------ + static float linearStepInOut( float _low0, float _high0, float _high1, float _low1, float _t ) + { + return linearStep( _low0, _high0, _t ) * (1.0f - linearStep( _high1, _low1, _t )); + } + + // ------------------------------------------------------------ + static float smoothStep(float edge0, float edge1, float x) + { + // Scale, and clamp x to 0..1 range + x = ofClamp( (x - edge0)/(edge1 - edge0), 0, 1); + // Evaluate polynomial + return x*x*x*(x*(x*6 - 15) + 10); + } + + // ------------------------------------------------------------ + static float smoothStepInOut( float _low0, float _high0, float _high1, float _low1, float _t ) + { + return smoothStep( _low0, _high0, _t ) * (1.0f - smoothStep( _high1, _low1, _t )); + } + + // ------------------------------------------------------------ + // Shaping functions + // ------------------------------------------------------------ + + // ------------------------------------------------------------ + static float pulseSquare( float _frequency, float _width, float _t ) + { + return 1 - step( _width, fmodf( _t, _frequency ) ); + } + + // ------------------------------------------------------------ + static float pulseTriangle( float _frequency, float _width, float _t ) + { + float triangleT = fmodf( _t, _frequency ) / _width * 2.0; + return (1.0 - fabs(fmodf(triangleT,2.0) - 1.0)) * pulseSquare( _frequency, _width, _t ); + } + + // ------------------------------------------------------------ + static float pulseLineDownUp( float _frequency, float _width, float _t ) + { + float tmpVal = fmodf( _t, _frequency ) / _width; + return tmpVal * (1 - step( 1.0, tmpVal )); + } + + // ------------------------------------------------------------ + static float pulseLineUpDown( float _frequency, float _width, float _t ) + { + float tmpVal = 1 - (fmodf( _t, _frequency ) / _width); + return ofClamp( tmpVal * (1 - step( 1.0, tmpVal )), 0, 1); + } + + // ------------------------------------------------------------ + static float pulseSawTooth( float _frequency, float _width, float _t ) + { + float tmpVal = 1 - (fmodf( _t, _frequency ) / _width); + return ofClamp( tmpVal * (1 - step( 1.0, tmpVal )), 0, 1); + } + + // ------------------------------------------------------------ + static float pulseSine( float _frequency, float _width, float _t ) + { + float tmpVal = ofClamp( (fmodf( _t, _frequency ) / _width), 0, 1); + return sinf(tmpVal * PI); + } + + // ----------------------------------------------------------- + static float pulseSmoothStep( float _frequency, float _x0, float _x1, float _x2, float _x3, float _t ) + { + float tmpT = fmodf( _t, _frequency ); + return smoothStepInOut( _x0, _x1, _x2, _x3, tmpT ); + } + + // ----------------------------------------------------------- + static float pulseLinearStep( float _frequency, float _x0, float _x1, float _x2, float _x3, float _t ) + { + float tmpT = fmodf( _t, _frequency ); + return linearStepInOut( _x0, _x1, _x2, _x3, tmpT ) ; + } + + // ------------------------------------------------------------ + // Misc + // ------------------------------------------------------------ + + // ------------------------------------------------------------ + static float getTriangleArea( ofVec3f _p0, ofVec3f _p1, ofVec3f _p2 ) + { + ofVec3f triangle1Side1 = _p0 - _p1; + ofVec3f triangle1Side2 = _p0 - _p2; + ofVec3f triangle1Side3 = _p1 - _p2; + float P1 = (triangle1Side1.length() + triangle1Side2.length() + triangle1Side3.length() ) / 2.0f; + return (float)sqrt(P1 * (P1-triangle1Side1.length()) * (P1-triangle1Side2.length()) * (P1-triangle1Side3.length()) ); + } + + // ------------------------------------------------------------ + static float horizontalToVerticalFov(float _horizontalfov, float _aspect ) // aspect = w/h + { + _horizontalfov = ofDegToRad(_horizontalfov); + float yfov = 2.0f * atanf(tanf(_horizontalfov * 0.5f) / _aspect); + return ofRadToDeg(yfov); + } + + // ------------------------------------------------------------ + static float verticalToHorizontalFov(float _verticalFov, float _aspect) + { + _verticalFov = ofDegToRad(_verticalFov); + float xfov = 2.0f * atanf(tanf(_verticalFov * 0.5f) * _aspect); + return ofRadToDeg(xfov); + } + + // ------------------------------------------------------------ + static unsigned int permuteQPR(unsigned int x) + { + static const unsigned int prime = 4294967291u; + if (x >= prime) + return x; // The 5 integers out of range are mapped to themselves. + unsigned int residue = ((unsigned long long) x * x) % prime; + return (x <= prime / 2) ? residue : prime - residue; + } + + // ------------------------------------------------------------ + static bool isInsideEllipse( ofVec2f _p, ofRectangle _ellipseRectangle ) + { + if( _ellipseRectangle.inside( _p ) ) + { + ofVec2f center = _ellipseRectangle.getPosition() + (ofVec2f(_ellipseRectangle.getWidth(), _ellipseRectangle.getHeight()) * 0.5f); + + float _xRadius = _ellipseRectangle.width * 0.5f; + float _yRadius = _ellipseRectangle.height * 0.5f; + + if (_xRadius <= 0.0 || _yRadius <= 0.0) + return false; + + ofVec2f normalized = ofVec2f(_p.x - center.x, + _p.y - center.y); + + return ((normalized.x * normalized.x) / (_xRadius * _xRadius)) + ((normalized.y * normalized.y) / (_yRadius * _yRadius)) + <= 1.0; + } + + return false; + } + + // ------------------------------------------------------------ + static ofVec3f randomPointOnSphere() + { + float lambda = ofRandom(1.0f); + float u = ofRandom(-1.0f, 1.0f); + float phi = ofRandom( 2.0 * PI ); + + ofVec3f p; + p.x = pow(lambda, 1/3) * sqrt(1.0 - u * u) * cos(phi); + p.y = pow(lambda, 1/3) * sqrt(1.0 - u * u) * sin(phi); + p.z = pow(lambda, 1/3) * u; + + return p; + } + + // ------------------------------------------------------------ + template + static float fbm( Vec _loc, int _octaves, float _lacunarity = 2.0, float _persistence = 0.5) + { + return (signedFbm( _loc, _octaves, _lacunarity, _persistence ) + 1.0) * 0.5; + } + + // ------------------------------------------------------------ + template + static float signedFbm( Vec _loc, int _octaves, float _lacunarity = 2.0, float _persistence = 0.5 ) + { + float finalNoise = 0.0; + float amplitude = 1.0; + float totalAmplitude = 0.0; + Vec tmpLoc = _loc; + + for( int i = 0; i < _octaves; i++) + { + amplitude *= _persistence; + totalAmplitude += amplitude; + float layerNoise = signedNoise(tmpLoc); + finalNoise += layerNoise * amplitude; + tmpLoc *= _lacunarity; // //sum += amp * snoise(pp); + } + + return finalNoise / totalAmplitude; + } + + // ------------------------------------------------------------ + // Noise shortcuts + // ------------------------------------------------------------ + + // ------------------------------------------------------------ + static float noise( float _p ) { return ofNoise( _p ); } + + // ------------------------------------------------------------ + static float noise( ofVec2f _p ) { return ofNoise( _p.x, _p.y ); } + + // ------------------------------------------------------------ + static float noise( ofVec3f _p ) { return ofNoise( _p.x, _p.y, _p.z ); } + + // ------------------------------------------------------------ + static float noise( ofVec4f _p ) { return ofNoise( _p.x, _p.y, _p.z, _p.w ); } + + // ------------------------------------------------------------ + static float signedNoise( float _p ) { return ofSignedNoise( _p ); } + + // ------------------------------------------------------------ + static float signedNoise( ofVec2f _p ) { return ofSignedNoise( _p.x, _p.y ); } + + // ------------------------------------------------------------ + static float signedNoise( ofVec3f _p ) { return ofSignedNoise( _p.x, _p.y, _p.z ); } + + // ------------------------------------------------------------ + static float signedNoise( ofVec4f _p ) { return ofSignedNoise( _p.x, _p.y, _p.z, _p.w ); } + + private: +}; diff --git a/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/Cameras/ofxFirstPersonCamera.h b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/Cameras/ofxFirstPersonCamera.h new file mode 100755 index 00000000000..1a19b9e669f --- /dev/null +++ b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/Cameras/ofxFirstPersonCamera.h @@ -0,0 +1,308 @@ +// +// FirstPersonCamera.h +// GaiaTerrain +// +// Created by Andreas Müller on 24/01/2014. +// +// + +#pragma once + +#include "ofMain.h" + +#include "ofAppGLFWWindow.h" + +class ofxFirstPersonCamera : public ofCamera +{ + public: + + // ---------------------------------------------- + ofxFirstPersonCamera() + { + forwardKey = 'w'; + backwardKey = 's'; + rightKey = 'a'; + leftKey = 'd'; + upKey = 'q'; + downKey = 'z'; + + movementMaxSpeed = 0.1f; + movementInertia = 0.15f; + movementDrag = 0.99f; + + mouseSensitivity = 0.3f; + rotationInertia = 0.2f; + rotationDrag = 0.8f; + + lastMouse = ofVec2f( ofGetMouseX(), ofGetMouseY() ); + + prevFrameNumReadJoystick = 0; + + useJoystick = false; + axisRotateX = 3; + axisRotateY = 2; + axisMoveForwards = 1; + axisMoveSideways = 0; + axisDeadZone = 0.15f; + rotationJoystickInertia = 0.4f; + + autoUpdate = false; + enableAutoUpdate(); + } + + // ---------------------------------------------- + ~ofxFirstPersonCamera() + { + disableAutoUpdate(); + } + + // ---------------------------------------------- + void enableAutoUpdate() + { + ofAddListener(ofEvents().update , this, &ofxFirstPersonCamera::update); + autoUpdate = true; + } + + // ---------------------------------------------- + void disableAutoUpdate() + { + if( autoUpdate ) + { + ofRemoveListener(ofEvents().update, this, &ofxFirstPersonCamera::update); + autoUpdate = false; + } + } + + // ---------------------------------------------- + virtual void update() + { + updateRotationMouse(); + updateTranslationKeyboard(); + + if( useJoystick ) + { + updateRotationJoystick(); + updateTranslationJoystick(); + } + } + + // ---------------------------------------------- + void updateRotationMouse( bool _constrainToYAxis = false ) + { + ofVec2f mouse = ofVec2f(ofGetMouseX(), ofGetMouseY()); + ofVec2f mouseVel = mouse - lastMouse; + lastMouse = mouse; + + // Rotation + rotationSpeed *= rotationDrag; + if( rotationSpeed.length() < 0.00000001f ) { rotationSpeed = ofVec3f(0,0,0); } + + if( ofGetMousePressed( OF_MOUSE_BUTTON_RIGHT) ) + { + rotationSpeed.x = ofLerp( rotationSpeed.x, mouseVel.y * mouseSensitivity, rotationInertia ); + rotationSpeed.y = ofLerp( rotationSpeed.y, mouseVel.x * mouseSensitivity, rotationInertia ); + } + + ofQuaternion tmpRotX( rotationSpeed.x, ofVec3f(1,0,0)); + ofQuaternion tmpRotY( rotationSpeed.y, ofVec3f(0,1,0)); + + // Todo: neater solution to this + if( _constrainToYAxis ) { setOrientation( getOrientationQuat() * tmpRotY ); } + else { setOrientation( tmpRotX * getOrientationQuat() * tmpRotY ); } + } + + // ---------------------------------------------- + void updateTranslationKeyboard() + { + // Translation + ofVec3f currPos = getPosition(); + + ofVec3f frameSpeed(0,0,0); // Todo: should we integrate over time as well? + + if( ofGetKeyPressed(forwardKey) ) { frameSpeed -= getZAxis() * movementMaxSpeed; } + if( ofGetKeyPressed(backwardKey) ) { frameSpeed += getZAxis() * movementMaxSpeed; } + + if( ofGetKeyPressed(rightKey) ) { frameSpeed -= getXAxis() * movementMaxSpeed; } + if( ofGetKeyPressed(leftKey) ) { frameSpeed += getXAxis() * movementMaxSpeed; } + + //cout << "getYAxis(): " << getYAxis(); + + if( ofGetKeyPressed(upKey) ) { frameSpeed += getYAxis() * movementMaxSpeed; } + if( ofGetKeyPressed(downKey) ) { frameSpeed -= getYAxis() * movementMaxSpeed; } + + movementSpeed.interpolate( frameSpeed, movementInertia ); + + setPosition( getPosition() + movementSpeed ); + movementSpeed *= movementDrag; + } + + // ---------------------------------------------- + void updateTranslationJoystick() + { + // Translation + ofVec3f currPos = getPosition(); + + ofVec3f frameSpeed(0,0,0); // Todo: should we integrate over time as well? + + float joystickAxisReadingForward = getJoystickAxis( axisMoveForwards, axisDeadZone ); + float joystickAxisReadingSideways = getJoystickAxis( axisMoveSideways, axisDeadZone ); + + frameSpeed -= getZAxis() * -joystickAxisReadingForward * movementMaxSpeed; + frameSpeed -= getXAxis() * -joystickAxisReadingSideways * movementMaxSpeed; + + movementSpeed.interpolate( frameSpeed, movementInertia ); + + setPosition( getPosition() + movementSpeed ); + //movementSpeed *= movementDrag; Todo: we only want to do this once for both + } + + // ---------------------------------------------- + void updateRotationJoystick( bool _constrainToYAxis = false ) + { + float joystickSensitivity = 1.0f; + + // Rotation + rotationJoystickSpeed *= rotationDrag; + if( rotationJoystickSpeed.length() < 0.00000001f ) { rotationJoystickSpeed = ofVec3f(0,0,0); } + + float joystickAxisReadingX = -getJoystickAxis( axisRotateX, axisDeadZone ); + float joystickAxisReadingY = -getJoystickAxis( axisRotateY, axisDeadZone ); + + if( abs(joystickAxisReadingX) > 0.0f ) rotationJoystickSpeed.x = ofLerp( rotationJoystickSpeed.x, joystickAxisReadingX * joystickSensitivity, rotationJoystickInertia ); + if( abs(joystickAxisReadingY) > 0.0f ) rotationJoystickSpeed.y = ofLerp( rotationJoystickSpeed.y, joystickAxisReadingY * joystickSensitivity * 2.0f, rotationJoystickInertia ); + + ofQuaternion tmpRotX( rotationJoystickSpeed.x, ofVec3f(1,0,0)); + ofQuaternion tmpRotY( rotationJoystickSpeed.y, ofVec3f(0,1,0)); + + // Todo: neater solution to this + if( _constrainToYAxis ) { setOrientation( getOrientationQuat() * tmpRotY ); } + else { setOrientation( tmpRotX * getOrientationQuat() * tmpRotY ); } + } + + // ---------------------------------------------- + char setForwardKey( char _forwardKey ) { forwardKey = _forwardKey; } + char setBackwardKey( char _backwardKey) { backwardKey = _backwardKey; } + char setRightKey( char _rightKey) { rightKey = _rightKey; } + char setLeftKey( char _leftKey) { leftKey = _leftKey; } + + // ---------------------------------------------- + char getForwardKey() { return forwardKey; } + char getBackwardKey() { return backwardKey; } + char getRightKey() { return rightKey; } + char getLeftKey() { return leftKey; } + + // ---------------------------------------------- + void setMovementMaxSpeed( float _movementMaxSpeed ) + { + movementMaxSpeed = _movementMaxSpeed; + } + + // ---------------------------------------------- + void setUseJoystick( bool _useJoystick ) + { + useJoystick = _useJoystick; + } + + // ---------------------------------------------- + bool getUseJoystick() + { + return useJoystick; + } + + protected: + + // ---------------------------------------------- + void update(ofEventArgs & args) + { + update(); + } + + // ---------------------------------------------- + float getJoystickAxis( int _axis, float _deadZone = 0.0f ) + { + pollJoystick(); // skips polling if we already polled this frame + + if( _axis < joystickAxis.size() ) + { + float axisReading = joystickAxis.at( _axis ); + int sign = ofSign( axisReading ); + + return ofMap( ofClamp( abs(axisReading) - _deadZone, 0.0f, 1.0f), 0.0f, 1.0f - _deadZone, 0.0f, 1.0f ) * sign; + } + + return 0.0f; + } + + // ---------------------------------------------- + bool isJoystickButtonPressed( int _button ) + { + pollJoystick(); // skips polling if we already polled this frame + + if( _button < joystickButtons.size() ) + { + return joystickButtons.at( _button ); + } + + return false; + } + + // ---------------------------------------------- + void pollJoystick() + { + if( prevFrameNumReadJoystick == ofGetFrameNum() ) + { + return; + } + + int joystickIndex = 0; + if (glfwJoystickPresent( joystickIndex ) == GL_TRUE) + { + int numButtons, numAxes; + const unsigned char *pressed = glfwGetJoystickButtons(joystickIndex, &numButtons); + const float *axes = glfwGetJoystickAxes(joystickIndex, &numAxes); + //printf("%d %d -- %p %p\n", nbuttons, naxes, pressed, axes); + + joystickButtons.clear(); + for( int i = 0; i < numButtons; i++ ) { joystickButtons.push_back( pressed[i] ); } + + joystickAxis.clear(); + for( int i = 0; i < numAxes; i++ ) { joystickAxis.push_back( axes[i] ); } + } + + prevFrameNumReadJoystick = ofGetFrameNum(); + } + + bool useJoystick; + int prevFrameNumReadJoystick; + float axisDeadZone; + vector joystickAxis; + vector joystickButtons; + int axisRotateX; + int axisRotateY; + int axisMoveForwards; + int axisMoveSideways; + float rotationJoystickInertia; + ofVec2f rotationJoystickSpeed; + + + bool autoUpdate; + + float mouseSensitivity; + float rotationInertia; + float rotationDrag; + ofVec2f rotationSpeed; + + float movementMaxSpeed; + float movementInertia; + float movementDrag; + ofVec3f movementSpeed; + + ofVec2f lastMouse; + + char forwardKey; + char backwardKey; + char rightKey; + char leftKey; + char upKey; + char downKey; +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/Cameras/ofxWalkingFirstPersonCamera.h b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/Cameras/ofxWalkingFirstPersonCamera.h new file mode 100755 index 00000000000..bac90be42bf --- /dev/null +++ b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/Cameras/ofxWalkingFirstPersonCamera.h @@ -0,0 +1,75 @@ +// +// ofxWalkingFirstPersonCamera.h +// GaiaTerrain +// +// Created by Andreas Müller on 26/01/2014. +// +// + +#pragma once + +#include "ofxFirstPersonCamera.h" + +class ofxWalkingFirstPersonCamera: public ofxFirstPersonCamera +{ + public: + + // ---------------------------------------------- + ofxWalkingFirstPersonCamera() + { + //setGravity( -9.8f ); + setGravity( 0.0f ); + setGroundLevelY( 0.0f ); + setEyeHeight( 0.8f ); + //cout << "ofxWalkingFirstPersonCamera()" << endl; + } + + // ---------------------------------------------- + void update() + { + ofxFirstPersonCamera::update(); + updatePhysics(); + } + + // ---------------------------------------------- + void updatePhysics() + { + ofVec3f currPos = getPosition(); + + // Todo: better physics model + currPos.y += gravity; + + // Todo: base collision check on planes + if( currPos.y < groundLevelY + eyeHeight ) + { + currPos.y = ofLerp( currPos.y, groundLevelY + eyeHeight, 0.5f ); + } + + setPosition( currPos ); + } + + // ---------------------------------------------- + void setGroundLevelY( float _groundLevelY ) + { + groundLevelY = _groundLevelY; + } + + // ---------------------------------------------- + void setEyeHeight( float _eyeHeight ) + { + eyeHeight = _eyeHeight; + } + + // ---------------------------------------------- + void setGravity( float _gravity ) + { + gravity = _gravity; + } + + protected: + + float gravity; + float groundLevelY; + float eyeHeight; + +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/Cameras/ofxWalkingFirstPersonCameraOculus.h b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/Cameras/ofxWalkingFirstPersonCameraOculus.h new file mode 100755 index 00000000000..effcf1d6f8f --- /dev/null +++ b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/Cameras/ofxWalkingFirstPersonCameraOculus.h @@ -0,0 +1,83 @@ +// +// ofxWalkingFirstPersonCameraOculus.h +// GaiaTerrain +// +// Created by Andreas Müller on 02/02/2014. +// +// + +#pragma once + +#include "ofxWalkingFirstPersonCamera.h" + +class ofxWalkingFirstPersonCameraOculus : public ofxWalkingFirstPersonCamera +{ + public: + + // ---------------------------------------------- + ofxWalkingFirstPersonCameraOculus() + { + constrainToYAxis = true; + disableAutoUpdate(); + } + + // ---------------------------------------------- + void update() + { + updateRotationMouse( constrainToYAxis ); + updateTranslationBasedOnHeadsetDirection(); + updatePhysics(); + } + + // ---------------------------------------------- + void updateTranslationBasedOnHeadsetDirection() + { + // Translation + ofVec3f currPos = getPosition(); + + ofVec3f forward = ofVec3f(0,0,1) * headsetOrientation * getOrientationQuat(); + ofVec3f sideways = ofVec3f(1,0,0) * headsetOrientation * getOrientationQuat(); + + ofVec3f frameSpeed(0,0,0); // Todo: should we integrate over time as well? + + if( ofGetKeyPressed(forwardKey) ) { frameSpeed -= forward * movementMaxSpeed; } + if( ofGetKeyPressed(backwardKey) ) { frameSpeed += forward * movementMaxSpeed; } + + if( ofGetKeyPressed(rightKey) ) { frameSpeed -= sideways * movementMaxSpeed; } + if( ofGetKeyPressed(leftKey) ) { frameSpeed += sideways * movementMaxSpeed; } + + movementSpeed.interpolate( frameSpeed, movementInertia ); + + setPosition( getPosition() + movementSpeed ); + movementSpeed *= movementDrag; + } + + // ---------------------------------------------- + void getHeadsetOrientation() + { + return headsetOrientation; + } + + // ---------------------------------------------- + void setHeadsetOrientation( ofQuaternion _headsetOrientation ) + { + headsetOrientation = _headsetOrientation; + } + + // ---------------------------------------------- + void setConstrainToYAxis( bool _constrainToYAxis ) + { + constrainToYAxis = _constrainToYAxis; + } + + // ---------------------------------------------- + void getConstrainToYAxis() + { + return constrainToYAxis; + } + + protected: + + ofQuaternion headsetOrientation; + bool constrainToYAxis; +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/FboPingPong.cpp b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/FboPingPong.cpp new file mode 100644 index 00000000000..16b8aee4728 --- /dev/null +++ b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/FboPingPong.cpp @@ -0,0 +1,115 @@ +// +// FboPingPong.cpp +// emptyExample +// +// Created by Andreas Müller on 12/08/2013. +// +// + +#include "FboPingPong.h" + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::allocate( int _w, int _h, int _internalformat, ofColor _clearColor ) +{ + ofFbo::Settings settings = ofFbo::Settings(); + settings.width = _w; + settings.height = _h; + settings.useDepth = true; + settings.internalformat = _internalformat; + + allocate( settings, _clearColor ); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::allocate( ofFbo::Settings _settings, ofColor _clearColor ) +{ + clearColor = _clearColor; + + fbo1.allocate( _settings); + fbo2.allocate( _settings ); + + sourceBuffer = &fbo1; + destBuffer = &fbo2; + + clearSource(); + clearDest(); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::draw( ofPoint _pos, float _width, bool _drawBack ) +{ + float desWidth = _width; + float desHeight = (source()->getWidth() / source()->getHeight()) * desWidth; + + source()->draw( _pos, desWidth, desHeight ); + + if( _drawBack ) + { + dest()->draw( _pos + ofVec2f(desWidth,0), desWidth, desHeight ); + } +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearBoth() +{ + clearSource(); + clearDest(); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearBoth( ofColor _clearColor ) +{ + clearSource( _clearColor ); + clearDest( _clearColor ); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearSource() +{ + clearSource( clearColor ); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearDest() +{ + clearDest( clearColor ); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearSource( ofColor _clearColor ) +{ + source()->begin(); + ofClear( _clearColor ); + source()->end(); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearDest( ofColor _clearColor ) +{ + dest()->begin(); + ofClear( _clearColor ); + dest()->end(); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::setClearColor( ofColor _color ) +{ + clearColor = _color; +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::swap() +{ + std::swap(sourceBuffer, destBuffer); +} \ No newline at end of file diff --git a/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/FboPingPong.h b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/FboPingPong.h new file mode 100644 index 00000000000..ac001de1f75 --- /dev/null +++ b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/FboPingPong.h @@ -0,0 +1,47 @@ +// +// FboPingPong.h +// emptyExample +// +// Created by Andreas Müller on 12/08/2013. +// +// + +#pragma once + +#include "ofMain.h" + +class FboPingPong +{ + public: + + void allocate( int _w, int _h, int internalformat = GL_RGB, ofColor _clearColor = ofColor(255,255,255) ); + void allocate( ofFbo::Settings _settings, ofColor _clearColor = ofColor(255,255,255) ); + + ofFbo* source() { return sourceBuffer; } + ofFbo* dest() { return destBuffer; } + + void draw( ofPoint _pos, float _width, bool _drawBack = false ); + + void clearBoth(); + void clearBoth( ofColor _clearColor ); + + void clearSource(); + void clearDest(); + + void clearSource( ofColor _clearColor ); + void clearDest( ofColor _clearColor ); + + void setClearColor( ofColor _color ); + + void swap(); + + private: + + ofFbo* sourceBuffer; + ofFbo* destBuffer; + + ofFbo fbo1; + ofFbo fbo2; + + ofColor clearColor; +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/ofTrueTypeFontExt.h b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/ofTrueTypeFontExt.h new file mode 100644 index 00000000000..f6c16734735 --- /dev/null +++ b/workshops/NoiseWorkshop/EmptyGPUParticles/src/Utils/ofTrueTypeFontExt.h @@ -0,0 +1,40 @@ +#pragma once + +#include "ofMain.h" + +class ofTrueTypeFontExt : public ofTrueTypeFont +{ + public: + + // ----------------------------------------------------------------- + void drawStringShadowed( string _s, ofVec2f _pos, + ofColor _frontColor = ofColor(255,255,255), ofColor _backColor = ofColor(0,0,0) ) + { + drawStringShadowed( _s, _pos.x, _pos.y, _frontColor, _backColor ); + } + + // ----------------------------------------------------------------- + void drawStringShadowed( string _s, float _x, float _y, + ofColor _frontColor = ofColor(255,255,255), ofColor _backColor = ofColor(0,0,0) ) + { + ofSetColor( _backColor ); + drawString( _s, _x + 1, _y + 1 ); + + ofSetColor( _frontColor ); + drawString( _s, _x, _y ); + } + + // ----------------------------------------------------------------- + void drawTextureAtlas( float _x, float _y, float _w, float _h ) + { + + if( _w == 0.0f || _h == 0.0f ) + { + _w = texAtlas.getWidth(); + _h = texAtlas.getHeight(); + } + + texAtlas.draw( _x, _y, _w, _h ); + } + +}; diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/Math/MathUtils.h b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Math/MathUtils.h new file mode 100644 index 00000000000..e3af4847937 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Math/MathUtils.h @@ -0,0 +1,242 @@ +#pragma once + +#include "ofMain.h" + +class MathUtils +{ + + public: + + // ------------------------------------------------------------ + // Step functions + // ------------------------------------------------------------ + + // ------------------------------------------------------------ + static float step(float a, float x) + { + return (float) (x >= a); + } + + // ------------------------------------------------------------ + static float linearStep( float _edge0, float _edge1, float _t ) + { + // Scale, and clamp x to 0..1 range + return ofClamp( (_t - _edge0)/(_edge1 - _edge0), 0.0f, 1.0f); + } + + // ------------------------------------------------------------ + static float linearStepInOut( float _low0, float _high0, float _high1, float _low1, float _t ) + { + return linearStep( _low0, _high0, _t ) * (1.0f - linearStep( _high1, _low1, _t )); + } + + // ------------------------------------------------------------ + static float smoothStep(float edge0, float edge1, float x) + { + // Scale, and clamp x to 0..1 range + x = ofClamp( (x - edge0)/(edge1 - edge0), 0, 1); + // Evaluate polynomial + return x*x*x*(x*(x*6 - 15) + 10); + } + + // ------------------------------------------------------------ + static float smoothStepInOut( float _low0, float _high0, float _high1, float _low1, float _t ) + { + return smoothStep( _low0, _high0, _t ) * (1.0f - smoothStep( _high1, _low1, _t )); + } + + // ------------------------------------------------------------ + // Shaping functions + // ------------------------------------------------------------ + + // ------------------------------------------------------------ + static float pulseSquare( float _frequency, float _width, float _t ) + { + return 1 - step( _width, fmodf( _t, _frequency ) ); + } + + // ------------------------------------------------------------ + static float pulseTriangle( float _frequency, float _width, float _t ) + { + float triangleT = fmodf( _t, _frequency ) / _width * 2.0; + return (1.0 - fabs(fmodf(triangleT,2.0) - 1.0)) * pulseSquare( _frequency, _width, _t ); + } + + // ------------------------------------------------------------ + static float pulseLineDownUp( float _frequency, float _width, float _t ) + { + float tmpVal = fmodf( _t, _frequency ) / _width; + return tmpVal * (1 - step( 1.0, tmpVal )); + } + + // ------------------------------------------------------------ + static float pulseLineUpDown( float _frequency, float _width, float _t ) + { + float tmpVal = 1 - (fmodf( _t, _frequency ) / _width); + return ofClamp( tmpVal * (1 - step( 1.0, tmpVal )), 0, 1); + } + + // ------------------------------------------------------------ + static float pulseSawTooth( float _frequency, float _width, float _t ) + { + float tmpVal = 1 - (fmodf( _t, _frequency ) / _width); + return ofClamp( tmpVal * (1 - step( 1.0, tmpVal )), 0, 1); + } + + // ------------------------------------------------------------ + static float pulseSine( float _frequency, float _width, float _t ) + { + float tmpVal = ofClamp( (fmodf( _t, _frequency ) / _width), 0, 1); + return sinf(tmpVal * PI); + } + + // ----------------------------------------------------------- + static float pulseSmoothStep( float _frequency, float _x0, float _x1, float _x2, float _x3, float _t ) + { + float tmpT = fmodf( _t, _frequency ); + return smoothStepInOut( _x0, _x1, _x2, _x3, tmpT ); + } + + // ----------------------------------------------------------- + static float pulseLinearStep( float _frequency, float _x0, float _x1, float _x2, float _x3, float _t ) + { + float tmpT = fmodf( _t, _frequency ); + return linearStepInOut( _x0, _x1, _x2, _x3, tmpT ) ; + } + + // ------------------------------------------------------------ + // Misc + // ------------------------------------------------------------ + + // ------------------------------------------------------------ + static float getTriangleArea( ofVec3f _p0, ofVec3f _p1, ofVec3f _p2 ) + { + ofVec3f triangle1Side1 = _p0 - _p1; + ofVec3f triangle1Side2 = _p0 - _p2; + ofVec3f triangle1Side3 = _p1 - _p2; + float P1 = (triangle1Side1.length() + triangle1Side2.length() + triangle1Side3.length() ) / 2.0f; + return (float)sqrt(P1 * (P1-triangle1Side1.length()) * (P1-triangle1Side2.length()) * (P1-triangle1Side3.length()) ); + } + + // ------------------------------------------------------------ + static float horizontalToVerticalFov(float _horizontalfov, float _aspect ) // aspect = w/h + { + _horizontalfov = ofDegToRad(_horizontalfov); + float yfov = 2.0f * atanf(tanf(_horizontalfov * 0.5f) / _aspect); + return ofRadToDeg(yfov); + } + + // ------------------------------------------------------------ + static float verticalToHorizontalFov(float _verticalFov, float _aspect) + { + _verticalFov = ofDegToRad(_verticalFov); + float xfov = 2.0f * atanf(tanf(_verticalFov * 0.5f) * _aspect); + return ofRadToDeg(xfov); + } + + // ------------------------------------------------------------ + static unsigned int permuteQPR(unsigned int x) + { + static const unsigned int prime = 4294967291u; + if (x >= prime) + return x; // The 5 integers out of range are mapped to themselves. + unsigned int residue = ((unsigned long long) x * x) % prime; + return (x <= prime / 2) ? residue : prime - residue; + } + + // ------------------------------------------------------------ + static bool isInsideEllipse( ofVec2f _p, ofRectangle _ellipseRectangle ) + { + if( _ellipseRectangle.inside( _p ) ) + { + ofVec2f center = _ellipseRectangle.getPosition() + (ofVec2f(_ellipseRectangle.getWidth(), _ellipseRectangle.getHeight()) * 0.5f); + + float _xRadius = _ellipseRectangle.width * 0.5f; + float _yRadius = _ellipseRectangle.height * 0.5f; + + if (_xRadius <= 0.0 || _yRadius <= 0.0) + return false; + + ofVec2f normalized = ofVec2f(_p.x - center.x, + _p.y - center.y); + + return ((normalized.x * normalized.x) / (_xRadius * _xRadius)) + ((normalized.y * normalized.y) / (_yRadius * _yRadius)) + <= 1.0; + } + + return false; + } + + // ------------------------------------------------------------ + static ofVec3f randomPointOnSphere() + { + float lambda = ofRandom(1.0f); + float u = ofRandom(-1.0f, 1.0f); + float phi = ofRandom( 2.0 * PI ); + + ofVec3f p; + p.x = pow(lambda, 1/3) * sqrt(1.0 - u * u) * cos(phi); + p.y = pow(lambda, 1/3) * sqrt(1.0 - u * u) * sin(phi); + p.z = pow(lambda, 1/3) * u; + + return p; + } + + // ------------------------------------------------------------ + template + static float fbm( Vec _loc, int _octaves, float _lacunarity = 2.0, float _persistence = 0.5) + { + return (signedFbm( _loc, _octaves, _lacunarity, _persistence ) + 1.0) * 0.5; + } + + // ------------------------------------------------------------ + template + static float signedFbm( Vec _loc, int _octaves, float _lacunarity = 2.0, float _persistence = 0.5 ) + { + float finalNoise = 0.0; + float amplitude = 1.0; + float totalAmplitude = 0.0; + Vec tmpLoc = _loc; + + for( int i = 0; i < _octaves; i++) + { + amplitude *= _persistence; + totalAmplitude += amplitude; + float layerNoise = signedNoise(tmpLoc); + finalNoise += layerNoise * amplitude; + tmpLoc *= _lacunarity; // //sum += amp * snoise(pp); + } + + return finalNoise / totalAmplitude; + } + + // ------------------------------------------------------------ + // Noise shortcuts + // ------------------------------------------------------------ + + // ------------------------------------------------------------ + static float noise( float _p ) { return ofNoise( _p ); } + + // ------------------------------------------------------------ + static float noise( ofVec2f _p ) { return ofNoise( _p.x, _p.y ); } + + // ------------------------------------------------------------ + static float noise( ofVec3f _p ) { return ofNoise( _p.x, _p.y, _p.z ); } + + // ------------------------------------------------------------ + static float noise( ofVec4f _p ) { return ofNoise( _p.x, _p.y, _p.z, _p.w ); } + + // ------------------------------------------------------------ + static float signedNoise( float _p ) { return ofSignedNoise( _p ); } + + // ------------------------------------------------------------ + static float signedNoise( ofVec2f _p ) { return ofSignedNoise( _p.x, _p.y ); } + + // ------------------------------------------------------------ + static float signedNoise( ofVec3f _p ) { return ofSignedNoise( _p.x, _p.y, _p.z ); } + + // ------------------------------------------------------------ + static float signedNoise( ofVec4f _p ) { return ofSignedNoise( _p.x, _p.y, _p.z, _p.w ); } + + private: +}; diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/ParticleSystemGPU.cpp b/workshops/NoiseWorkshop/ParticleCloudGPU/src/ParticleSystemGPU.cpp new file mode 100644 index 00000000000..b6587f61a72 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/ParticleSystemGPU.cpp @@ -0,0 +1,185 @@ +// +// ParticleSystemGPU.cpp +// ParticlesGPU +// +// Created by Andreas Müller on 11/01/2015. +// +// + +#include "ParticleSystemGPU.h" + +//----------------------------------------------------------------------------------------- +// +void ParticleSystemGPU::init( int _texSize ) +{ + string xmlSettingsPath = "Settings/Main.xml"; + gui.setup( "Main", xmlSettingsPath ); + gui.add( particleMaxAge.set("Particle Max Age", 10.0f, 0.0f, 20.0f) ); + gui.add( noiseMagnitude.set("Noise Magnitude", 0.075, 0.01f, 2.0f) ); + gui.add( noisePositionScale.set("Noise Position Scale", 1.5f, 0.01f, 10.0f) ); + gui.add( noiseTimeScale.set("Noise Time Scale", 1.0 / 4000.0, 0.001f, 1.0f) ); + gui.add( noisePersistence.set("Noise Persistence", 0.2, 0.001f, 1.0f) ); + gui.add( baseSpeed.set("Wind", ofVec3f(0.5,0,0), ofVec3f(-2,-2,-2), ofVec3f(2,2,2)) ); + gui.add( startColor.set("Start Color", ofColor::white, ofColor(0,0,0,0), ofColor(255,255,255,255)) ); + gui.add( endColor.set("End Color", ofColor(0,0,0,0), ofColor(0,0,0,0), ofColor(255,255,255,255)) ); + gui.add( particleSize.set("Particle Size", 0.01, 0.0001f, 0.05f) ); + //gui.add( twistNoiseTimeScale.set("Twist Noise Time Scale", 0.01, 0.0f, 0.5f) ); + //gui.add( twistNoisePosScale.set("Twist Noise Pos Scale", 0.25, 0.0f, 2.0f) ); + //gui.add( twistMinAng.set("Twist Min Ang", -1, -5, 5) ); + //gui.add( twistMaxAng.set("Twist Max Ang", 2.5, -5, 5) ); + + gui.loadFromFile( xmlSettingsPath ); + + + // To use a texture with point sprites it will need to be created as a GL_TEXTURE_2D and not a GL_TEXTURE_RECTANGLE + // This way it has texture coordinates in the range 0..1 instead of 0..imagewith + ofDisableArbTex(); + particleImage.loadImage( "Textures/Soft64.png" ); + ofEnableArbTex(); + + // Load shaders +#ifdef TARGET_OPENGLES + particleUpdate.load("Shaders/Particles/GLES/Update"); + particleDrawUnsorted.load("Shaders/Particles/GLES/DrawUnsorted"); +#else + particleUpdate.load("Shaders/Particles/GL2/Update"); + particleDrawUnsorted.load("Shaders/Particles/GL2/DrawUnsorted"); +#endif + + + // Set how many particles we are going to have, this is based on data texture size + textureSize = 400; + numParticles = textureSize * textureSize; + + // Allocate buffers + ofFbo::Settings fboSettings; + fboSettings.width = textureSize; + fboSettings.height = textureSize; + + // We can create several color buffers for one FBO if we want to store velocity for instance, + // then draw to them simultaneously from a shader using gl_FragData[0], gl_FragData[1], etc. + fboSettings.numColorbuffers = 1; + + fboSettings.useDepth = false; + fboSettings.internalformat = GL_RGBA32F; // Gotta store the data as floats, they won't be clamped to 0..1 + fboSettings.textureTarget = GL_TEXTURE_2D; + fboSettings.wrapModeHorizontal = GL_CLAMP_TO_EDGE; + fboSettings.wrapModeVertical = GL_CLAMP_TO_EDGE; + fboSettings.minFilter = GL_NEAREST; // No interpolation, that would mess up data reads later! + fboSettings.maxFilter = GL_NEAREST; + + ofDisableTextureEdgeHack(); + + particleDataFbo.allocate( fboSettings ); + + ofEnableTextureEdgeHack(); + + // Buffer 1: XYZ pos, W age + // Buffer 2: XYZ vel + + // Initialise the starting and static data + ofVec4f* startPositionsAndAge = new ofVec4f[numParticles]; + + // We also create a vbo that has the texture coordinate for each particle's data + particlePoints.setMode( OF_PRIMITIVE_POINTS ); + + int tmpIndex = 0; + for( int y = 0; y < textureSize; y++ ) + { + for( int x = 0; x < textureSize; x++ ) + { + float size = 0; + ofVec3f pos (MathUtils::randomPointOnSphere() * 0.1); + pos.set( ofRandom(-1,1), ofRandom(0,2), ofRandom(-1,1) ); + float startAge = ofRandom( particleMaxAge ); + + startPositionsAndAge[tmpIndex] = ofVec4f( pos.x, pos.y, pos.z, startAge ); + + ofVec2f texCoord; + texCoord.x = ofMap( x + 0.5f, 0, textureSize, 0.0f, 1.0f ); // the original source has a '+ 0.5' in it, to get the ceil? + texCoord.y = ofMap( y + 0.5f, 0, textureSize, 0.0f, 1.0f ); + + particlePoints.addVertex( ofVec3f(x,y,0) ); // this cuould be 0,0,0 as we won't use it, but put somehting in here so you can draw it without a shader to confirm it draws + particlePoints.addTexCoord( texCoord ); + tmpIndex++; + } + } + + // Upload it to the source texture + particleDataFbo.source()->getTextureReference(0).loadData( (float*)&startPositionsAndAge[0].x, textureSize, textureSize, GL_RGBA ); + +} + +//----------------------------------------------------------------------------------------- +// +void ParticleSystemGPU::update( float _time, float _timeStep ) +{ + ofEnableBlendMode( OF_BLENDMODE_DISABLED ); // Important! We just want to write the data as is to the target fbo + + particleDataFbo.dest()->begin(); + + particleDataFbo.dest()->activateAllDrawBuffers(); // if we have multiple color buffers in our FBO we need this to activate all of them + + particleUpdate.begin(); + + particleUpdate.setUniformTexture( "u_positionAndAgeTex", particleDataFbo.source()->getTextureReference(0), 0 ); + //particleUpdate.setUniformTexture( "velocityTex", particleDataFbo.source()->getTextureReference(1), 1 ); + + particleUpdate.setUniform1f("u_time", _time ); + particleUpdate.setUniform1f("u_timeStep", _timeStep ); + + particleUpdate.setUniform1f("u_particleMaxAge", particleMaxAge ); + + particleUpdate.setUniform1f("u_noisePositionScale", noisePositionScale ); + particleUpdate.setUniform1f("u_noiseTimeScale", noiseTimeScale ); + particleUpdate.setUniform1f("u_noisePersistence", noisePersistence ); + particleUpdate.setUniform1f("u_noiseMagnitude", noiseMagnitude ); + particleUpdate.setUniform3f("u_wind", baseSpeed.get().x, baseSpeed.get().y, baseSpeed.get().z ); + + particleDataFbo.source()->draw(0,0); + + particleUpdate.end(); + + particleDataFbo.dest()->end(); + + particleDataFbo.swap(); +} + +//----------------------------------------------------------------------------------------- +// +void ParticleSystemGPU::draw( ofCamera* _camera ) +{ + ofFloatColor particleStartCol = startColor.get(); + ofFloatColor particleEndCol = endColor.get(); + + ofSetColor( ofColor::white ); + ofEnableBlendMode( OF_BLENDMODE_ADD ); + //ofEnableBlendMode( OF_BLENDMODE_ALPHA ); + + ofEnablePointSprites(); + + particleDrawUnsorted.begin(); + + particleDrawUnsorted.setUniform2f("u_resolution", particleImage.getWidth(), particleImage.getHeight() ); + + particleDrawUnsorted.setUniformTexture("u_particleImageTexture", particleImage.getTextureReference(), 0 ); + particleDrawUnsorted.setUniformTexture("u_particleDataTexture", particleDataFbo.source()->getTextureReference(), 1 ); + + particleDrawUnsorted.setUniformMatrix4f("u_viewMatrix", _camera->getModelViewMatrix() ); + particleDrawUnsorted.setUniformMatrix4f("u_projectionMatrix", _camera->getProjectionMatrix() ); + particleDrawUnsorted.setUniformMatrix4f("u_modelViewProjectionMatrix", _camera->getModelViewProjectionMatrix() ); + + particleDrawUnsorted.setUniform1f("u_particleMaxAge", particleMaxAge ); + + particleDrawUnsorted.setUniform1f("u_particleDiameter", particleSize ); + particleDrawUnsorted.setUniform1f("u_screenWidth", ofGetWidth() ); + + particleDrawUnsorted.setUniform4fv("u_particleStartColor", particleStartCol.v ); + particleDrawUnsorted.setUniform4fv("u_particleEndColor", particleEndCol.v ); + + particlePoints.draw(); + + particleDrawUnsorted.end(); + + ofDisablePointSprites(); +} \ No newline at end of file diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/ParticleSystemGPU.h b/workshops/NoiseWorkshop/ParticleCloudGPU/src/ParticleSystemGPU.h new file mode 100644 index 00000000000..1edf5032a11 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/ParticleSystemGPU.h @@ -0,0 +1,57 @@ +// +// ParticleSystemGPU.h +// ParticlesGPU +// +// Created by Andreas Müller on 11/01/2015. +// +// + +#pragma once + +#include "ofMain.h" +#include "ofxGui.h" +#include "ofxAutoReloadedShader.h" + +#include "Math/MathUtils.h" +#include "Utils/FboPingPong.h" + +class ParticleSystemGPU +{ + + public: + + void init( int _texSize ); + void update( float _time, float _timeStep ); + void draw( ofCamera* _camera ); + + int numParticles; + int textureSize; + + FboPingPong particleDataFbo; + + ofVboMesh particlePoints; + + ofxAutoReloadedShader particleUpdate; + ofxAutoReloadedShader particleDrawUnsorted; + + ofImage particleImage; + + ofxPanel gui; + ofParameter particleMaxAge; + ofParameter noisePositionScale; + ofParameter noiseMagnitude; + ofParameter noiseTimeScale; + ofParameter noisePersistence; + ofParameter twistNoiseTimeScale; + ofParameter twistNoisePosScale; + ofParameter twistMinAng; + ofParameter twistMaxAng; + + ofParameter baseSpeed; + + ofParameter startColor; + ofParameter endColor; + + ofParameter particleSize; + +}; diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/Cameras/ofxFirstPersonCamera.h b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/Cameras/ofxFirstPersonCamera.h new file mode 100755 index 00000000000..1a19b9e669f --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/Cameras/ofxFirstPersonCamera.h @@ -0,0 +1,308 @@ +// +// FirstPersonCamera.h +// GaiaTerrain +// +// Created by Andreas Müller on 24/01/2014. +// +// + +#pragma once + +#include "ofMain.h" + +#include "ofAppGLFWWindow.h" + +class ofxFirstPersonCamera : public ofCamera +{ + public: + + // ---------------------------------------------- + ofxFirstPersonCamera() + { + forwardKey = 'w'; + backwardKey = 's'; + rightKey = 'a'; + leftKey = 'd'; + upKey = 'q'; + downKey = 'z'; + + movementMaxSpeed = 0.1f; + movementInertia = 0.15f; + movementDrag = 0.99f; + + mouseSensitivity = 0.3f; + rotationInertia = 0.2f; + rotationDrag = 0.8f; + + lastMouse = ofVec2f( ofGetMouseX(), ofGetMouseY() ); + + prevFrameNumReadJoystick = 0; + + useJoystick = false; + axisRotateX = 3; + axisRotateY = 2; + axisMoveForwards = 1; + axisMoveSideways = 0; + axisDeadZone = 0.15f; + rotationJoystickInertia = 0.4f; + + autoUpdate = false; + enableAutoUpdate(); + } + + // ---------------------------------------------- + ~ofxFirstPersonCamera() + { + disableAutoUpdate(); + } + + // ---------------------------------------------- + void enableAutoUpdate() + { + ofAddListener(ofEvents().update , this, &ofxFirstPersonCamera::update); + autoUpdate = true; + } + + // ---------------------------------------------- + void disableAutoUpdate() + { + if( autoUpdate ) + { + ofRemoveListener(ofEvents().update, this, &ofxFirstPersonCamera::update); + autoUpdate = false; + } + } + + // ---------------------------------------------- + virtual void update() + { + updateRotationMouse(); + updateTranslationKeyboard(); + + if( useJoystick ) + { + updateRotationJoystick(); + updateTranslationJoystick(); + } + } + + // ---------------------------------------------- + void updateRotationMouse( bool _constrainToYAxis = false ) + { + ofVec2f mouse = ofVec2f(ofGetMouseX(), ofGetMouseY()); + ofVec2f mouseVel = mouse - lastMouse; + lastMouse = mouse; + + // Rotation + rotationSpeed *= rotationDrag; + if( rotationSpeed.length() < 0.00000001f ) { rotationSpeed = ofVec3f(0,0,0); } + + if( ofGetMousePressed( OF_MOUSE_BUTTON_RIGHT) ) + { + rotationSpeed.x = ofLerp( rotationSpeed.x, mouseVel.y * mouseSensitivity, rotationInertia ); + rotationSpeed.y = ofLerp( rotationSpeed.y, mouseVel.x * mouseSensitivity, rotationInertia ); + } + + ofQuaternion tmpRotX( rotationSpeed.x, ofVec3f(1,0,0)); + ofQuaternion tmpRotY( rotationSpeed.y, ofVec3f(0,1,0)); + + // Todo: neater solution to this + if( _constrainToYAxis ) { setOrientation( getOrientationQuat() * tmpRotY ); } + else { setOrientation( tmpRotX * getOrientationQuat() * tmpRotY ); } + } + + // ---------------------------------------------- + void updateTranslationKeyboard() + { + // Translation + ofVec3f currPos = getPosition(); + + ofVec3f frameSpeed(0,0,0); // Todo: should we integrate over time as well? + + if( ofGetKeyPressed(forwardKey) ) { frameSpeed -= getZAxis() * movementMaxSpeed; } + if( ofGetKeyPressed(backwardKey) ) { frameSpeed += getZAxis() * movementMaxSpeed; } + + if( ofGetKeyPressed(rightKey) ) { frameSpeed -= getXAxis() * movementMaxSpeed; } + if( ofGetKeyPressed(leftKey) ) { frameSpeed += getXAxis() * movementMaxSpeed; } + + //cout << "getYAxis(): " << getYAxis(); + + if( ofGetKeyPressed(upKey) ) { frameSpeed += getYAxis() * movementMaxSpeed; } + if( ofGetKeyPressed(downKey) ) { frameSpeed -= getYAxis() * movementMaxSpeed; } + + movementSpeed.interpolate( frameSpeed, movementInertia ); + + setPosition( getPosition() + movementSpeed ); + movementSpeed *= movementDrag; + } + + // ---------------------------------------------- + void updateTranslationJoystick() + { + // Translation + ofVec3f currPos = getPosition(); + + ofVec3f frameSpeed(0,0,0); // Todo: should we integrate over time as well? + + float joystickAxisReadingForward = getJoystickAxis( axisMoveForwards, axisDeadZone ); + float joystickAxisReadingSideways = getJoystickAxis( axisMoveSideways, axisDeadZone ); + + frameSpeed -= getZAxis() * -joystickAxisReadingForward * movementMaxSpeed; + frameSpeed -= getXAxis() * -joystickAxisReadingSideways * movementMaxSpeed; + + movementSpeed.interpolate( frameSpeed, movementInertia ); + + setPosition( getPosition() + movementSpeed ); + //movementSpeed *= movementDrag; Todo: we only want to do this once for both + } + + // ---------------------------------------------- + void updateRotationJoystick( bool _constrainToYAxis = false ) + { + float joystickSensitivity = 1.0f; + + // Rotation + rotationJoystickSpeed *= rotationDrag; + if( rotationJoystickSpeed.length() < 0.00000001f ) { rotationJoystickSpeed = ofVec3f(0,0,0); } + + float joystickAxisReadingX = -getJoystickAxis( axisRotateX, axisDeadZone ); + float joystickAxisReadingY = -getJoystickAxis( axisRotateY, axisDeadZone ); + + if( abs(joystickAxisReadingX) > 0.0f ) rotationJoystickSpeed.x = ofLerp( rotationJoystickSpeed.x, joystickAxisReadingX * joystickSensitivity, rotationJoystickInertia ); + if( abs(joystickAxisReadingY) > 0.0f ) rotationJoystickSpeed.y = ofLerp( rotationJoystickSpeed.y, joystickAxisReadingY * joystickSensitivity * 2.0f, rotationJoystickInertia ); + + ofQuaternion tmpRotX( rotationJoystickSpeed.x, ofVec3f(1,0,0)); + ofQuaternion tmpRotY( rotationJoystickSpeed.y, ofVec3f(0,1,0)); + + // Todo: neater solution to this + if( _constrainToYAxis ) { setOrientation( getOrientationQuat() * tmpRotY ); } + else { setOrientation( tmpRotX * getOrientationQuat() * tmpRotY ); } + } + + // ---------------------------------------------- + char setForwardKey( char _forwardKey ) { forwardKey = _forwardKey; } + char setBackwardKey( char _backwardKey) { backwardKey = _backwardKey; } + char setRightKey( char _rightKey) { rightKey = _rightKey; } + char setLeftKey( char _leftKey) { leftKey = _leftKey; } + + // ---------------------------------------------- + char getForwardKey() { return forwardKey; } + char getBackwardKey() { return backwardKey; } + char getRightKey() { return rightKey; } + char getLeftKey() { return leftKey; } + + // ---------------------------------------------- + void setMovementMaxSpeed( float _movementMaxSpeed ) + { + movementMaxSpeed = _movementMaxSpeed; + } + + // ---------------------------------------------- + void setUseJoystick( bool _useJoystick ) + { + useJoystick = _useJoystick; + } + + // ---------------------------------------------- + bool getUseJoystick() + { + return useJoystick; + } + + protected: + + // ---------------------------------------------- + void update(ofEventArgs & args) + { + update(); + } + + // ---------------------------------------------- + float getJoystickAxis( int _axis, float _deadZone = 0.0f ) + { + pollJoystick(); // skips polling if we already polled this frame + + if( _axis < joystickAxis.size() ) + { + float axisReading = joystickAxis.at( _axis ); + int sign = ofSign( axisReading ); + + return ofMap( ofClamp( abs(axisReading) - _deadZone, 0.0f, 1.0f), 0.0f, 1.0f - _deadZone, 0.0f, 1.0f ) * sign; + } + + return 0.0f; + } + + // ---------------------------------------------- + bool isJoystickButtonPressed( int _button ) + { + pollJoystick(); // skips polling if we already polled this frame + + if( _button < joystickButtons.size() ) + { + return joystickButtons.at( _button ); + } + + return false; + } + + // ---------------------------------------------- + void pollJoystick() + { + if( prevFrameNumReadJoystick == ofGetFrameNum() ) + { + return; + } + + int joystickIndex = 0; + if (glfwJoystickPresent( joystickIndex ) == GL_TRUE) + { + int numButtons, numAxes; + const unsigned char *pressed = glfwGetJoystickButtons(joystickIndex, &numButtons); + const float *axes = glfwGetJoystickAxes(joystickIndex, &numAxes); + //printf("%d %d -- %p %p\n", nbuttons, naxes, pressed, axes); + + joystickButtons.clear(); + for( int i = 0; i < numButtons; i++ ) { joystickButtons.push_back( pressed[i] ); } + + joystickAxis.clear(); + for( int i = 0; i < numAxes; i++ ) { joystickAxis.push_back( axes[i] ); } + } + + prevFrameNumReadJoystick = ofGetFrameNum(); + } + + bool useJoystick; + int prevFrameNumReadJoystick; + float axisDeadZone; + vector joystickAxis; + vector joystickButtons; + int axisRotateX; + int axisRotateY; + int axisMoveForwards; + int axisMoveSideways; + float rotationJoystickInertia; + ofVec2f rotationJoystickSpeed; + + + bool autoUpdate; + + float mouseSensitivity; + float rotationInertia; + float rotationDrag; + ofVec2f rotationSpeed; + + float movementMaxSpeed; + float movementInertia; + float movementDrag; + ofVec3f movementSpeed; + + ofVec2f lastMouse; + + char forwardKey; + char backwardKey; + char rightKey; + char leftKey; + char upKey; + char downKey; +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/Cameras/ofxWalkingFirstPersonCamera.h b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/Cameras/ofxWalkingFirstPersonCamera.h new file mode 100755 index 00000000000..bac90be42bf --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/Cameras/ofxWalkingFirstPersonCamera.h @@ -0,0 +1,75 @@ +// +// ofxWalkingFirstPersonCamera.h +// GaiaTerrain +// +// Created by Andreas Müller on 26/01/2014. +// +// + +#pragma once + +#include "ofxFirstPersonCamera.h" + +class ofxWalkingFirstPersonCamera: public ofxFirstPersonCamera +{ + public: + + // ---------------------------------------------- + ofxWalkingFirstPersonCamera() + { + //setGravity( -9.8f ); + setGravity( 0.0f ); + setGroundLevelY( 0.0f ); + setEyeHeight( 0.8f ); + //cout << "ofxWalkingFirstPersonCamera()" << endl; + } + + // ---------------------------------------------- + void update() + { + ofxFirstPersonCamera::update(); + updatePhysics(); + } + + // ---------------------------------------------- + void updatePhysics() + { + ofVec3f currPos = getPosition(); + + // Todo: better physics model + currPos.y += gravity; + + // Todo: base collision check on planes + if( currPos.y < groundLevelY + eyeHeight ) + { + currPos.y = ofLerp( currPos.y, groundLevelY + eyeHeight, 0.5f ); + } + + setPosition( currPos ); + } + + // ---------------------------------------------- + void setGroundLevelY( float _groundLevelY ) + { + groundLevelY = _groundLevelY; + } + + // ---------------------------------------------- + void setEyeHeight( float _eyeHeight ) + { + eyeHeight = _eyeHeight; + } + + // ---------------------------------------------- + void setGravity( float _gravity ) + { + gravity = _gravity; + } + + protected: + + float gravity; + float groundLevelY; + float eyeHeight; + +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/Cameras/ofxWalkingFirstPersonCameraOculus.h b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/Cameras/ofxWalkingFirstPersonCameraOculus.h new file mode 100755 index 00000000000..effcf1d6f8f --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/Cameras/ofxWalkingFirstPersonCameraOculus.h @@ -0,0 +1,83 @@ +// +// ofxWalkingFirstPersonCameraOculus.h +// GaiaTerrain +// +// Created by Andreas Müller on 02/02/2014. +// +// + +#pragma once + +#include "ofxWalkingFirstPersonCamera.h" + +class ofxWalkingFirstPersonCameraOculus : public ofxWalkingFirstPersonCamera +{ + public: + + // ---------------------------------------------- + ofxWalkingFirstPersonCameraOculus() + { + constrainToYAxis = true; + disableAutoUpdate(); + } + + // ---------------------------------------------- + void update() + { + updateRotationMouse( constrainToYAxis ); + updateTranslationBasedOnHeadsetDirection(); + updatePhysics(); + } + + // ---------------------------------------------- + void updateTranslationBasedOnHeadsetDirection() + { + // Translation + ofVec3f currPos = getPosition(); + + ofVec3f forward = ofVec3f(0,0,1) * headsetOrientation * getOrientationQuat(); + ofVec3f sideways = ofVec3f(1,0,0) * headsetOrientation * getOrientationQuat(); + + ofVec3f frameSpeed(0,0,0); // Todo: should we integrate over time as well? + + if( ofGetKeyPressed(forwardKey) ) { frameSpeed -= forward * movementMaxSpeed; } + if( ofGetKeyPressed(backwardKey) ) { frameSpeed += forward * movementMaxSpeed; } + + if( ofGetKeyPressed(rightKey) ) { frameSpeed -= sideways * movementMaxSpeed; } + if( ofGetKeyPressed(leftKey) ) { frameSpeed += sideways * movementMaxSpeed; } + + movementSpeed.interpolate( frameSpeed, movementInertia ); + + setPosition( getPosition() + movementSpeed ); + movementSpeed *= movementDrag; + } + + // ---------------------------------------------- + void getHeadsetOrientation() + { + return headsetOrientation; + } + + // ---------------------------------------------- + void setHeadsetOrientation( ofQuaternion _headsetOrientation ) + { + headsetOrientation = _headsetOrientation; + } + + // ---------------------------------------------- + void setConstrainToYAxis( bool _constrainToYAxis ) + { + constrainToYAxis = _constrainToYAxis; + } + + // ---------------------------------------------- + void getConstrainToYAxis() + { + return constrainToYAxis; + } + + protected: + + ofQuaternion headsetOrientation; + bool constrainToYAxis; +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/DrawingHelpers.h b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/DrawingHelpers.h new file mode 100644 index 00000000000..f369767b96e --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/DrawingHelpers.h @@ -0,0 +1,74 @@ +// +// DrawingHelpers.h +// FirstPersonCamera +// +// Created by Andreas Müller on 22/09/2014. +// +// + +#pragma once + +#include "ofMain.h" + +class DrawingHelpers +{ + public: + + //----------------------------------------------------------------------------------------- + // + static void drawFloorGrid( ofTrueTypeFont* _font ) + { + ofSetColor( ofColor(50) ); + + ofPushMatrix(); + ofRotate(90, 0, 0, -1); + ofDrawGridPlane( 10, 1, false ); + ofPopMatrix(); + + ofDisableDepthTest(); + + ofSetColor( ofColor(100) ); + + float tickDistMm = 1000; + int numTicks = 20; + ofVec2f start( ((numTicks * -0.5) * tickDistMm), ((numTicks * -0.5) * tickDistMm) ); + ofVec2f end( ((numTicks * 0.5) * tickDistMm), ((numTicks * 0.5) * tickDistMm) ); + for( int y = 0; y <= numTicks; y++ ) + { + float tmpY = start.y + (y * tickDistMm); + + ofLine( ofVec3f( start.x, 0, tmpY), ofVec3f( end.x, 0, tmpY) ); + + for( int x = 0; x <= numTicks; x++ ) + { + float tmpX = start.x + (x * tickDistMm); + ofLine( ofVec3f( tmpX, 0, start.y), ofVec3f( tmpX, 0, end.y) ); + } + } + + ofSetColor( ofColor(150) ); + + for( int y = 0; y <= numTicks; y++ ) + { + float tmpY = start.y + (y * tickDistMm); + + ofPushMatrix(); + ofTranslate( ofVec3f(-10,0,tmpY + 10) ); + ofRotateX(-90); + ofScale(2,2,2); + //fontLarge.drawString( ofToString(tmpY), 0, 0 ); + if( _font != NULL ) _font->drawString( ofToString(tmpY), 0, 0 ); + ofPopMatrix(); + + ofPushMatrix(); + ofTranslate( ofVec3f(tmpY + 10,0, -10) ); + ofRotateX(-90); + ofScale(2,2,2); + //fontLarge.drawString( ofToString(tmpY), 0, 0 ); + if( _font != NULL ) _font->drawString( ofToString(tmpY), 0, 0 ); + ofPopMatrix(); + } + + ofEnableDepthTest(); + } +}; diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/FboPingPong.cpp b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/FboPingPong.cpp new file mode 100644 index 00000000000..16b8aee4728 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/FboPingPong.cpp @@ -0,0 +1,115 @@ +// +// FboPingPong.cpp +// emptyExample +// +// Created by Andreas Müller on 12/08/2013. +// +// + +#include "FboPingPong.h" + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::allocate( int _w, int _h, int _internalformat, ofColor _clearColor ) +{ + ofFbo::Settings settings = ofFbo::Settings(); + settings.width = _w; + settings.height = _h; + settings.useDepth = true; + settings.internalformat = _internalformat; + + allocate( settings, _clearColor ); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::allocate( ofFbo::Settings _settings, ofColor _clearColor ) +{ + clearColor = _clearColor; + + fbo1.allocate( _settings); + fbo2.allocate( _settings ); + + sourceBuffer = &fbo1; + destBuffer = &fbo2; + + clearSource(); + clearDest(); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::draw( ofPoint _pos, float _width, bool _drawBack ) +{ + float desWidth = _width; + float desHeight = (source()->getWidth() / source()->getHeight()) * desWidth; + + source()->draw( _pos, desWidth, desHeight ); + + if( _drawBack ) + { + dest()->draw( _pos + ofVec2f(desWidth,0), desWidth, desHeight ); + } +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearBoth() +{ + clearSource(); + clearDest(); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearBoth( ofColor _clearColor ) +{ + clearSource( _clearColor ); + clearDest( _clearColor ); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearSource() +{ + clearSource( clearColor ); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearDest() +{ + clearDest( clearColor ); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearSource( ofColor _clearColor ) +{ + source()->begin(); + ofClear( _clearColor ); + source()->end(); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearDest( ofColor _clearColor ) +{ + dest()->begin(); + ofClear( _clearColor ); + dest()->end(); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::setClearColor( ofColor _color ) +{ + clearColor = _color; +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::swap() +{ + std::swap(sourceBuffer, destBuffer); +} \ No newline at end of file diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/FboPingPong.h b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/FboPingPong.h new file mode 100644 index 00000000000..ac001de1f75 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/FboPingPong.h @@ -0,0 +1,47 @@ +// +// FboPingPong.h +// emptyExample +// +// Created by Andreas Müller on 12/08/2013. +// +// + +#pragma once + +#include "ofMain.h" + +class FboPingPong +{ + public: + + void allocate( int _w, int _h, int internalformat = GL_RGB, ofColor _clearColor = ofColor(255,255,255) ); + void allocate( ofFbo::Settings _settings, ofColor _clearColor = ofColor(255,255,255) ); + + ofFbo* source() { return sourceBuffer; } + ofFbo* dest() { return destBuffer; } + + void draw( ofPoint _pos, float _width, bool _drawBack = false ); + + void clearBoth(); + void clearBoth( ofColor _clearColor ); + + void clearSource(); + void clearDest(); + + void clearSource( ofColor _clearColor ); + void clearDest( ofColor _clearColor ); + + void setClearColor( ofColor _color ); + + void swap(); + + private: + + ofFbo* sourceBuffer; + ofFbo* destBuffer; + + ofFbo fbo1; + ofFbo fbo2; + + ofColor clearColor; +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/FileUtils.h b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/FileUtils.h new file mode 100644 index 00000000000..da4f26611f3 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/FileUtils.h @@ -0,0 +1,93 @@ +// +// FileUtils.h +// ParticleMaker +// +// Created by Andreas Müller on 04/03/2014. +// +// + +#pragma once + +class FileUtils +{ + public: + + // --------------------------------------------------------------------------------------------------------------------------------------------------- + // + static vector getFilePathsInFolder( string _folder, string _extension ) + { + vector tmpExt; + tmpExt.push_back( _extension ); + return getFilePathsInFolder( _folder, tmpExt ); + } + + // --------------------------------------------------------------------------------------------------------------------------------------------------- + // + static vector getFilePathsInFolder( string _folder, vector _extensions ) + { + vector filePaths; + + ofDirectory tmpDir( _folder ); + for( unsigned int i = 0; i < _extensions.size(); i++ ) + { + tmpDir.allowExt( _extensions.at(i) ); + } + + int numFiles = tmpDir.listDir( _folder ); + for( int i = 0; i < numFiles; i++ ) + { + filePaths.push_back( tmpDir.getPath(i) ); + } + + return filePaths; + } + + // --------------------------------------------------------------------------------------------------------------------------------------------------- + // + static string getFirstFileOfTypeInFolder( string _folder, string _extension ) + { + vector tmpFiles = getFilePathsInFolder( _folder, _extension ); + if( tmpFiles.size() > 0 ) + { + return tmpFiles.at(0); + } + return ""; + } + + // --------------------------------------------------------------------------------------------------------------------------------------------------- + // + static vector getImageFilePathsInFolder( string _folder ) + { + vector tmpExt; + tmpExt.push_back( "png" ); + tmpExt.push_back( "bmp" ); + tmpExt.push_back( "jpg" ); + tmpExt.push_back( "tga" ); + + return getFilePathsInFolder( _folder, tmpExt ); + } + + // --------------------------------------------------------------------------------------------------------------------------------------------------- + // + static void loadImagesInFolder( string _folder, string _extension, vector& _images ) + { + vector tmpExt; + tmpExt.push_back( _extension ); + return loadImagesInFolder( _folder, tmpExt, _images ); + } + + // --------------------------------------------------------------------------------------------------------------------------------------------------- + // + static void loadImagesInFolder( string _folder, vector _extensions, vector& _images ) + { + vector textureFilePaths = FileUtils::getFilePathsInFolder( _folder, _extensions ); + + for( unsigned int i = 0; i < textureFilePaths.size(); i++ ) + { + ofImage* tmpIm = new ofImage(); + tmpIm->loadImage( textureFilePaths.at(i) ); + _images.push_back( tmpIm ); + } + } + +}; diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/SizeLimitedDeque.h b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/SizeLimitedDeque.h new file mode 100644 index 00000000000..47c8e17dcba --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/SizeLimitedDeque.h @@ -0,0 +1,99 @@ +// +// SizeLimitedDeque.h +// LostSignalTrackingNew +// +// Created by Andreas Müller on 26/08/2014. +// +// + +#pragma once + +#include +using namespace std; + +template +class SizeLimitedDeque +{ + public: + + // ----------------------------------------- + SizeLimitedDeque() + { + maxSize = -1; + } + + // ----------------------------------------- + void pushFront (const T& _val) + { + dat.push_front( _val ); + limitSizeFromBack(); + } + + // ----------------------------------------- + void pushBack (const T& _val) + { + dat.push_back( _val ); + limitSizeFromFront(); + } + + // ----------------------------------------- + T at( int _index ) + { + return dat.at(_index); + } + + // ----------------------------------------- + size_t size() + { + return dat.size(); + } + + // ----------------------------------------- + void setMaxSize( int _size ) + { + maxSize = _size; + } + + + // ----------------------------------------- + int getMaxSize() + { + return maxSize; + } + + deque dat; + + protected: + + // ----------------------------------------- + void limitSizeFromBack() + { + if( maxSize > -1 ) + { + if( dat.size() > maxSize ) + { + while( dat.size() > maxSize ) + { + dat.pop_back(); + } + } + } + } + + // ----------------------------------------- + void limitSizeFromFront() + { + if( maxSize > -1 ) + { + if( dat.size() > maxSize ) + { + while( dat.size() > maxSize ) + { + dat.pop_front(); + } + } + } + } + + int maxSize; +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/ofEasyCamExt.cpp b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/ofEasyCamExt.cpp new file mode 100644 index 00000000000..2ad4dcc8190 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/ofEasyCamExt.cpp @@ -0,0 +1,402 @@ +#include "ofEasyCamExt.h" +#include "ofMath.h" +#include "ofUtils.h" + +//---------------------------------------- +ofEasyCamExt::ofEasyCamExt() +{ + lastTap = 0; + lastDistance = 0; + drag = 0.9f; + + sensitivityRot = 0.01f; //when 1 moving the mouse from one side to the other of the arcball (min(viewport.width, viewport.height)) will rotate 180degrees. when .5, 90 degrees. + sensitivityXY = 0.5; + sensitivityZ = 0.01; + + bDistanceSet = false; + bMouseInputEnabled = false; + bDoRotate = false; + bApplyInertia =false; + bDoTranslate = false; + bInsideArcball = true; + bValidClick = false; + bEnableMouseMiddleButton = true; + bAutoDistance = true; + + doTranslationKey = 0; + doDollyKey = 0; + + dollyForwardKey = 'a'; + dollyBackwardKey = 'z'; + + dollyImpulseAmount = 0.01f; + maxDollyImpulseSpeed = 1.0f; + + reset(); + enableMouseInput(); + +} + +//---------------------------------------- +ofEasyCamExt::~ofEasyCamExt(){ + disableMouseInput(); +} +//---------------------------------------- +void ofEasyCamExt::update(ofEventArgs & args) +{ + + if(!bDistanceSet && bAutoDistance) + { + setDistance(getImagePlaneDistance(viewport), true); + } + + if(bMouseInputEnabled) + { + rotationFactor = sensitivityRot * 180 / min(viewport.width, viewport.height); + if (bMouseInputEnabled) + { + updateMouse(); + } + + if (bDoRotate) + { + updateRotation(); + } + else if (bDoTranslate) + { + updateTranslation(); + } + } + + if( dollyForwardKey != 0 ) + { + if( ofGetKeyPressed(dollyForwardKey) ) { dollyImpulse( -dollyImpulseAmount ); } + } + + if( dollyBackwardKey != 0 ) + { + if( ofGetKeyPressed(dollyBackwardKey) ) { dollyImpulse( dollyImpulseAmount ); } + } + + float minDifference = 0.1e-5; + +// if (bApplyInertia) { + moveX *= drag; + moveY *= drag; + moveZ *= drag; + if (ABS(moveX) <= minDifference && ABS(moveY) <= minDifference && ABS(moveZ) <= minDifference) { + //bApplyInertia = false; + bDoTranslate = false; + } +// } + + move((getXAxis() * moveX) + (getYAxis() * moveY) + (getZAxis() * moveZ)); + + +} +//---------------------------------------- +void ofEasyCamExt::begin(ofRectangle viewport){ + this->viewport = viewport; + ofCamera::begin(viewport); +} + +//---------------------------------------- +void ofEasyCamExt::reset(){ + target.resetTransform(); + + target.setPosition(0,0, 0); + lookAt(target); + + resetTransform(); + setPosition(0, 0, lastDistance); + + + xRot = 0; + yRot = 0; + zRot = 0; + + moveX = 0; + moveY = 0; + moveZ = 0; +} +//---------------------------------------- +void ofEasyCamExt::setTarget(const ofVec3f& targetPoint){ + target.setPosition(targetPoint); + lookAt(target); +} +//---------------------------------------- +void ofEasyCamExt::setTarget(ofNode& targetNode){ + target = targetNode; + lookAt(target); +} +//---------------------------------------- +ofNode& ofEasyCamExt::getTarget(){ + return target; +} +//---------------------------------------- +void ofEasyCamExt::setDistance(float distance){ + setDistance(distance, true); +} +//---------------------------------------- +void ofEasyCamExt::setDistance(float distance, bool save){//should this be the distance from the camera to the target? + if (distance > 0.0f){ + if(save){ + this->lastDistance = distance; + } + setPosition(target.getPosition() + (distance * getZAxis())); + bDistanceSet = true; + } +} +//---------------------------------------- +float ofEasyCamExt::getDistance() const { + return target.getPosition().distance(getPosition()); +} +//---------------------------------------- +void ofEasyCamExt::setAutoDistance(bool bAutoDistance){ + this->bAutoDistance = bAutoDistance; + if (bAutoDistance) { + bDistanceSet = false; + } +} +//---------------------------------------- +void ofEasyCamExt::setDrag(float drag){ + this->drag = drag; +} +//---------------------------------------- +float ofEasyCamExt::getDrag() const { + return drag; +} +//---------------------------------------- +void ofEasyCamExt::setTranslationKey(char key){ + doTranslationKey = key; +} +//---------------------------------------- +char ofEasyCamExt::getTranslationKey(){ + return doTranslationKey; +} + +//---------------------------------------- +void ofEasyCamExt::setDollyKey(char _key) { + doDollyKey = _key; +} + +//---------------------------------------- +char ofEasyCamExt::getDollyKey() { + return doDollyKey; +} + +//---------------------------------------- +void ofEasyCamExt::setDollyForwardKey(char _key) +{ + dollyForwardKey = _key; +} + +//---------------------------------------- +char ofEasyCamExt::getDollyForwardKey() +{ + return dollyForwardKey; +} + +//---------------------------------------- +void ofEasyCamExt::setDollyBackwardKey(char _key) +{ + dollyBackwardKey = _key; +} + +//---------------------------------------- +char ofEasyCamExt::getDollyBackwardKey() +{ + return dollyBackwardKey; +} + +//---------------------------------------- +void ofEasyCamExt::setDollyImpulseMagnitude( float _impulseMagnitude ) +{ + dollyImpulseAmount = _impulseMagnitude; + maxDollyImpulseSpeed = dollyImpulseAmount * 4.0f; +} + +//---------------------------------------- +float ofEasyCamExt::getDollyImpulseMagnitude() +{ + return dollyImpulseAmount; +} + +//---------------------------------------- +void ofEasyCamExt::dollyImpulse( float _impulse ){ + + //cout << "dollyImpulse: " << _impulse << endl;; + + moveZ += _impulse; + moveZ = ofClamp( moveZ, -maxDollyImpulseSpeed, maxDollyImpulseSpeed ); +} + +//---------------------------------------- +void ofEasyCamExt::enableMouseInput(){ + if(!bMouseInputEnabled){ + bMouseInputEnabled = true; + ofAddListener(ofEvents().update , this, &ofEasyCamExt::update); + } +} +//---------------------------------------- +void ofEasyCamExt::disableMouseInput(){ + if(bMouseInputEnabled){ + bMouseInputEnabled = false; + ofRemoveListener(ofEvents().update, this, &ofEasyCamExt::update); + } +} + +//---------------------------------------- +bool ofEasyCamExt::getMouseInputEnabled(){ + return bMouseInputEnabled; +} + +//---------------------------------------- +void ofEasyCamExt::enableMouseMiddleButton(){ + bEnableMouseMiddleButton = true; +} + +//---------------------------------------- +void ofEasyCamExt::disableMouseMiddleButton(){ + bEnableMouseMiddleButton = false; +} + +//---------------------------------------- +bool ofEasyCamExt::getMouseMiddleButtonEnabled(){ + return bEnableMouseMiddleButton; +} + +//---------------------------------------- +void ofEasyCamExt::updateTranslation() +{ + move((getXAxis() * moveX) + (getYAxis() * moveY) + (getZAxis() * moveZ)); +} + +//---------------------------------------- +void ofEasyCamExt::updateRotation(){ + if (bApplyInertia) { + xRot *=drag; + yRot *=drag; + zRot *=drag; + + float minDifference = 0.1e-5; + + if (ABS(xRot) <= minDifference && ABS(yRot) <= minDifference && ABS(zRot) <= minDifference) { + bApplyInertia = false; + bDoRotate = false; + } + } + curRot = ofQuaternion(xRot, ofCamera::getXAxis(), yRot, ofCamera::getYAxis(), zRot, ofCamera::getZAxis()); + setPosition((ofCamera::getGlobalPosition()-target.getGlobalPosition())*curRot +target.getGlobalPosition()); + rotate(curRot); +} +//---------------------------------------- +void ofEasyCamExt::updateMouse(){ + + + mouse = ofVec2f(ofGetMouseX(), ofGetMouseY()); + + if(viewport.inside(mouse.x, mouse.y) && !bValidClick && (ofGetMousePressed(OF_MOUSE_BUTTON_RIGHT) || ofGetMousePressed(OF_MOUSE_BUTTON_MIDDLE)) ) + { + unsigned long doubleclickTime = 200; + unsigned long curTap = ofGetElapsedTimeMillis(); + if(lastTap != 0 && curTap - lastTap < doubleclickTime) + { + reset(); + } + + if ((bEnableMouseMiddleButton && ofGetMousePressed(OF_MOUSE_BUTTON_MIDDLE)) || + ofGetKeyPressed(doTranslationKey) || + ofGetKeyPressed(doDollyKey) /*|| ofGetMousePressed(OF_MOUSE_BUTTON_RIGHT)*/ ) + { + bDoTranslate = true; + bDoRotate = false; + bApplyInertia = false; + } + else if (ofGetMousePressed(OF_MOUSE_BUTTON_RIGHT) ) + { + bDoTranslate = false; + bDoRotate = true; + bApplyInertia = false; + if(ofVec2f(mouse.x - viewport.x - (viewport.width/2), mouse.y - viewport.y - (viewport.height/2)).length() < min(viewport.width/2, viewport.height/2)) + { + bInsideArcball = true; + } + else + { + bInsideArcball = false; + } + } + + lastTap = curTap; + //lastMouse = ofVec2f(ofGetPreviousMouseX(),ofGetPreviousMouseY()); //this was causing the camera to have a tiny "random" rotation when clicked. + lastMouse = mouse; + bValidClick = true; + bApplyInertia = false; + } + + + if (bValidClick) + { + if ( !(ofGetMousePressed(OF_MOUSE_BUTTON_RIGHT) || ofGetMousePressed(OF_MOUSE_BUTTON_MIDDLE)) ) + { + bApplyInertia = true; + bValidClick = false; + } + else + { + int vFlip; + if(isVFlipped()){ + vFlip = -1; + }else{ + vFlip = 1; + } + + mouseVel = mouse - lastMouse; + + if (bDoTranslate) + { + moveX = 0; + moveY = 0; + //moveZ = 0; + + //if ( ofGetMousePressed(OF_MOUSE_BUTTON_RIGHT)) + if ( ofGetKeyPressed(doDollyKey) ) + { + //moveZ = mouseVel.y * sensitivityZ * (getDistance() + FLT_EPSILON)/ viewport.height; + moveZ += mouseVel.y * sensitivityZ * (getDistance() + FLT_EPSILON)/ viewport.height; + } + else + { + moveX = -mouseVel.x * sensitivityXY * (getDistance() + FLT_EPSILON)/viewport.width; + moveY = vFlip * mouseVel.y * sensitivityXY * (getDistance() + FLT_EPSILON)/viewport.height; + } + } + else + { + //xRot = 0; + //yRot = 0; + //zRot = 0; + + if (bInsideArcball) + { + //xRot = vFlip * -mouseVel.y * rotationFactor; + //yRot = -mouseVel.x * rotationFactor; + + xRot += vFlip * -mouseVel.y * rotationFactor; + yRot += -mouseVel.x * rotationFactor; + } + else + { + ofVec2f center(viewport.width/2, viewport.height/2); + //zRot = -vFlip * ofVec2f(mouse.x - viewport.x - center.x, mouse.y - viewport.y - center.y).angle(lastMouse - ofVec2f(viewport.x, viewport.y) - center); + zRot += (-vFlip * ofVec2f(mouse.x - viewport.x - center.x, mouse.y - viewport.y - center.y).angle(lastMouse - ofVec2f(viewport.x, viewport.y) - center)) * sensitivityRot; + } + } + lastMouse = mouse; + } + } + +} + + diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/ofEasyCamExt.h b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/ofEasyCamExt.h new file mode 100644 index 00000000000..546a12bda5d --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/ofEasyCamExt.h @@ -0,0 +1,122 @@ +#pragma once + +#include "ofParameter.h" +#include "ofCamera.h" +#include "ofEvents.h" + +/* + 99% Memo's ofEasyCam, I just needed to change a few small things and was having trouvle doing it by subclassing. + */ + +class ofEasyCamExt : public ofCamera +{ + public: + + ofEasyCamExt(); + ~ofEasyCamExt(); + + // TODO: this should be ofGetViewRect() eventually + virtual void begin(ofRectangle viewport = ofGetCurrentViewport()); + void reset(); + + //---------------------------------------- + // advanced functions + + void setTarget(const ofVec3f& target); + void setTarget(ofNode& target); + ofNode& getTarget(); + + void setDistance(float distance); + float getDistance() const; + + // drag is how quickly the camera picks up and slows down + // it is a normalized value between 0-1 + void setDrag(float drag); + float getDrag() const; + // the translation key is the key used to switch between rotation and translation. + // translation happens only when the key is pressed. + void setTranslationKey(char key); + char getTranslationKey(); + + void setDollyKey(char _key); + char getDollyKey(); + + void setDollyForwardKey(char _key); + char getDollyForwardKey(); + + void setDollyBackwardKey(char _key); + char getDollyBackwardKey(); + + void setDollyImpulseMagnitude( float _impulseMagnitude ); + float getDollyImpulseMagnitude(); + + void dollyImpulse( float _impulse ); + + // enable or disable mouse input to navigate + void enableMouseInput(); + void disableMouseInput(); + bool getMouseInputEnabled(); + + void enableMouseMiddleButton(); + void disableMouseMiddleButton(); + bool getMouseMiddleButtonEnabled(); + + void setAutoDistance(bool bAutoDistance); + + protected: + + void setDistance(float distance, bool save); + + ofNode target; + + bool bEnableMouseMiddleButton; + bool bApplyInertia; + bool bDoTranslate; + bool bDoRotate; + bool bValidClick; + bool bInsideArcball; + bool bMouseInputEnabled; + bool bDistanceSet; + bool bAutoDistance; + float lastDistance; + + float drag; + + float xRot; + float yRot; + float zRot; + + float moveX; + float moveY; + float moveZ; + + float sensitivityXY; + float sensitivityZ; + float sensitivityRot; + + float rotationFactor; + + ofVec2f mouse; + ofVec2f lastMouse; + ofVec2f mouseVel; + + void updateRotation(); + void updateTranslation(); + void update(ofEventArgs & args); + void updateMouse(); + + char doTranslationKey; + char doDollyKey; + + char dollyForwardKey; + char dollyBackwardKey; + + float dollyImpulseAmount; + float maxDollyImpulseSpeed; + + unsigned long lastTap; + + ofQuaternion curRot; + + ofRectangle viewport;// having the viewport saved localy will make it easier for all the needed maths dealing with viewport. +}; diff --git a/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/ofTrueTypeFontExt.h b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/ofTrueTypeFontExt.h new file mode 100644 index 00000000000..f6c16734735 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleCloudGPU/src/Utils/ofTrueTypeFontExt.h @@ -0,0 +1,40 @@ +#pragma once + +#include "ofMain.h" + +class ofTrueTypeFontExt : public ofTrueTypeFont +{ + public: + + // ----------------------------------------------------------------- + void drawStringShadowed( string _s, ofVec2f _pos, + ofColor _frontColor = ofColor(255,255,255), ofColor _backColor = ofColor(0,0,0) ) + { + drawStringShadowed( _s, _pos.x, _pos.y, _frontColor, _backColor ); + } + + // ----------------------------------------------------------------- + void drawStringShadowed( string _s, float _x, float _y, + ofColor _frontColor = ofColor(255,255,255), ofColor _backColor = ofColor(0,0,0) ) + { + ofSetColor( _backColor ); + drawString( _s, _x + 1, _y + 1 ); + + ofSetColor( _frontColor ); + drawString( _s, _x, _y ); + } + + // ----------------------------------------------------------------- + void drawTextureAtlas( float _x, float _y, float _w, float _h ) + { + + if( _w == 0.0f || _h == 0.0f ) + { + _w = texAtlas.getWidth(); + _h = texAtlas.getHeight(); + } + + texAtlas.draw( _x, _y, _w, _h ); + } + +}; diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Math/MathUtils.h b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Math/MathUtils.h new file mode 100644 index 00000000000..e3af4847937 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Math/MathUtils.h @@ -0,0 +1,242 @@ +#pragma once + +#include "ofMain.h" + +class MathUtils +{ + + public: + + // ------------------------------------------------------------ + // Step functions + // ------------------------------------------------------------ + + // ------------------------------------------------------------ + static float step(float a, float x) + { + return (float) (x >= a); + } + + // ------------------------------------------------------------ + static float linearStep( float _edge0, float _edge1, float _t ) + { + // Scale, and clamp x to 0..1 range + return ofClamp( (_t - _edge0)/(_edge1 - _edge0), 0.0f, 1.0f); + } + + // ------------------------------------------------------------ + static float linearStepInOut( float _low0, float _high0, float _high1, float _low1, float _t ) + { + return linearStep( _low0, _high0, _t ) * (1.0f - linearStep( _high1, _low1, _t )); + } + + // ------------------------------------------------------------ + static float smoothStep(float edge0, float edge1, float x) + { + // Scale, and clamp x to 0..1 range + x = ofClamp( (x - edge0)/(edge1 - edge0), 0, 1); + // Evaluate polynomial + return x*x*x*(x*(x*6 - 15) + 10); + } + + // ------------------------------------------------------------ + static float smoothStepInOut( float _low0, float _high0, float _high1, float _low1, float _t ) + { + return smoothStep( _low0, _high0, _t ) * (1.0f - smoothStep( _high1, _low1, _t )); + } + + // ------------------------------------------------------------ + // Shaping functions + // ------------------------------------------------------------ + + // ------------------------------------------------------------ + static float pulseSquare( float _frequency, float _width, float _t ) + { + return 1 - step( _width, fmodf( _t, _frequency ) ); + } + + // ------------------------------------------------------------ + static float pulseTriangle( float _frequency, float _width, float _t ) + { + float triangleT = fmodf( _t, _frequency ) / _width * 2.0; + return (1.0 - fabs(fmodf(triangleT,2.0) - 1.0)) * pulseSquare( _frequency, _width, _t ); + } + + // ------------------------------------------------------------ + static float pulseLineDownUp( float _frequency, float _width, float _t ) + { + float tmpVal = fmodf( _t, _frequency ) / _width; + return tmpVal * (1 - step( 1.0, tmpVal )); + } + + // ------------------------------------------------------------ + static float pulseLineUpDown( float _frequency, float _width, float _t ) + { + float tmpVal = 1 - (fmodf( _t, _frequency ) / _width); + return ofClamp( tmpVal * (1 - step( 1.0, tmpVal )), 0, 1); + } + + // ------------------------------------------------------------ + static float pulseSawTooth( float _frequency, float _width, float _t ) + { + float tmpVal = 1 - (fmodf( _t, _frequency ) / _width); + return ofClamp( tmpVal * (1 - step( 1.0, tmpVal )), 0, 1); + } + + // ------------------------------------------------------------ + static float pulseSine( float _frequency, float _width, float _t ) + { + float tmpVal = ofClamp( (fmodf( _t, _frequency ) / _width), 0, 1); + return sinf(tmpVal * PI); + } + + // ----------------------------------------------------------- + static float pulseSmoothStep( float _frequency, float _x0, float _x1, float _x2, float _x3, float _t ) + { + float tmpT = fmodf( _t, _frequency ); + return smoothStepInOut( _x0, _x1, _x2, _x3, tmpT ); + } + + // ----------------------------------------------------------- + static float pulseLinearStep( float _frequency, float _x0, float _x1, float _x2, float _x3, float _t ) + { + float tmpT = fmodf( _t, _frequency ); + return linearStepInOut( _x0, _x1, _x2, _x3, tmpT ) ; + } + + // ------------------------------------------------------------ + // Misc + // ------------------------------------------------------------ + + // ------------------------------------------------------------ + static float getTriangleArea( ofVec3f _p0, ofVec3f _p1, ofVec3f _p2 ) + { + ofVec3f triangle1Side1 = _p0 - _p1; + ofVec3f triangle1Side2 = _p0 - _p2; + ofVec3f triangle1Side3 = _p1 - _p2; + float P1 = (triangle1Side1.length() + triangle1Side2.length() + triangle1Side3.length() ) / 2.0f; + return (float)sqrt(P1 * (P1-triangle1Side1.length()) * (P1-triangle1Side2.length()) * (P1-triangle1Side3.length()) ); + } + + // ------------------------------------------------------------ + static float horizontalToVerticalFov(float _horizontalfov, float _aspect ) // aspect = w/h + { + _horizontalfov = ofDegToRad(_horizontalfov); + float yfov = 2.0f * atanf(tanf(_horizontalfov * 0.5f) / _aspect); + return ofRadToDeg(yfov); + } + + // ------------------------------------------------------------ + static float verticalToHorizontalFov(float _verticalFov, float _aspect) + { + _verticalFov = ofDegToRad(_verticalFov); + float xfov = 2.0f * atanf(tanf(_verticalFov * 0.5f) * _aspect); + return ofRadToDeg(xfov); + } + + // ------------------------------------------------------------ + static unsigned int permuteQPR(unsigned int x) + { + static const unsigned int prime = 4294967291u; + if (x >= prime) + return x; // The 5 integers out of range are mapped to themselves. + unsigned int residue = ((unsigned long long) x * x) % prime; + return (x <= prime / 2) ? residue : prime - residue; + } + + // ------------------------------------------------------------ + static bool isInsideEllipse( ofVec2f _p, ofRectangle _ellipseRectangle ) + { + if( _ellipseRectangle.inside( _p ) ) + { + ofVec2f center = _ellipseRectangle.getPosition() + (ofVec2f(_ellipseRectangle.getWidth(), _ellipseRectangle.getHeight()) * 0.5f); + + float _xRadius = _ellipseRectangle.width * 0.5f; + float _yRadius = _ellipseRectangle.height * 0.5f; + + if (_xRadius <= 0.0 || _yRadius <= 0.0) + return false; + + ofVec2f normalized = ofVec2f(_p.x - center.x, + _p.y - center.y); + + return ((normalized.x * normalized.x) / (_xRadius * _xRadius)) + ((normalized.y * normalized.y) / (_yRadius * _yRadius)) + <= 1.0; + } + + return false; + } + + // ------------------------------------------------------------ + static ofVec3f randomPointOnSphere() + { + float lambda = ofRandom(1.0f); + float u = ofRandom(-1.0f, 1.0f); + float phi = ofRandom( 2.0 * PI ); + + ofVec3f p; + p.x = pow(lambda, 1/3) * sqrt(1.0 - u * u) * cos(phi); + p.y = pow(lambda, 1/3) * sqrt(1.0 - u * u) * sin(phi); + p.z = pow(lambda, 1/3) * u; + + return p; + } + + // ------------------------------------------------------------ + template + static float fbm( Vec _loc, int _octaves, float _lacunarity = 2.0, float _persistence = 0.5) + { + return (signedFbm( _loc, _octaves, _lacunarity, _persistence ) + 1.0) * 0.5; + } + + // ------------------------------------------------------------ + template + static float signedFbm( Vec _loc, int _octaves, float _lacunarity = 2.0, float _persistence = 0.5 ) + { + float finalNoise = 0.0; + float amplitude = 1.0; + float totalAmplitude = 0.0; + Vec tmpLoc = _loc; + + for( int i = 0; i < _octaves; i++) + { + amplitude *= _persistence; + totalAmplitude += amplitude; + float layerNoise = signedNoise(tmpLoc); + finalNoise += layerNoise * amplitude; + tmpLoc *= _lacunarity; // //sum += amp * snoise(pp); + } + + return finalNoise / totalAmplitude; + } + + // ------------------------------------------------------------ + // Noise shortcuts + // ------------------------------------------------------------ + + // ------------------------------------------------------------ + static float noise( float _p ) { return ofNoise( _p ); } + + // ------------------------------------------------------------ + static float noise( ofVec2f _p ) { return ofNoise( _p.x, _p.y ); } + + // ------------------------------------------------------------ + static float noise( ofVec3f _p ) { return ofNoise( _p.x, _p.y, _p.z ); } + + // ------------------------------------------------------------ + static float noise( ofVec4f _p ) { return ofNoise( _p.x, _p.y, _p.z, _p.w ); } + + // ------------------------------------------------------------ + static float signedNoise( float _p ) { return ofSignedNoise( _p ); } + + // ------------------------------------------------------------ + static float signedNoise( ofVec2f _p ) { return ofSignedNoise( _p.x, _p.y ); } + + // ------------------------------------------------------------ + static float signedNoise( ofVec3f _p ) { return ofSignedNoise( _p.x, _p.y, _p.z ); } + + // ------------------------------------------------------------ + static float signedNoise( ofVec4f _p ) { return ofSignedNoise( _p.x, _p.y, _p.z, _p.w ); } + + private: +}; diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/ParticleSystemInstancedGeometryGPU.cpp b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/ParticleSystemInstancedGeometryGPU.cpp new file mode 100644 index 00000000000..2f8f8fa4824 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/ParticleSystemInstancedGeometryGPU.cpp @@ -0,0 +1,210 @@ +// +// ParticleSystemGPU.cpp +// ParticlesGPU +// +// Created by Andreas Müller on 11/01/2015. +// +// + +#include "ParticleSystemInstancedGeometryGPU.h" + +//----------------------------------------------------------------------------------------- +// +void ParticleSystemInstancedGeometryGPU::init( int _texSize ) +{ + string xmlSettingsPath = "Settings/Particles.xml"; + gui.setup( "Particles", xmlSettingsPath ); + gui.add( particleMaxAge.set("Particle Max Age", 10.0f, 0.0f, 20.0f) ); + gui.add( noiseMagnitude.set("Noise Magnitude", 0.075, 0.01f, 1.0f) ); + gui.add( noisePositionScale.set("Noise Position Scale", 1.5f, 0.01f, 5.0f) ); + gui.add( noiseTimeScale.set("Noise Time Scale", 1.0 / 4000.0, 0.001f, 1.0f) ); + gui.add( noisePersistence.set("Noise Persistence", 0.2, 0.001f, 1.0f) ); + gui.add( baseSpeed.set("Wind", ofVec3f(0.5,0,0), ofVec3f(-2,-2,-2), ofVec3f(2,2,2)) ); + gui.add( startColor.set("Start Color", ofColor::white, ofColor(0,0,0,0), ofColor(255,255,255,255)) ); + gui.add( endColor.set("End Color", ofColor(0,0,0,0), ofColor(0,0,0,0), ofColor(255,255,255,255)) ); + //gui.add( twistNoiseTimeScale.set("Twist Noise Time Scale", 0.01, 0.0f, 0.5f) ); + //gui.add( twistNoisePosScale.set("Twist Noise Pos Scale", 0.25, 0.0f, 2.0f) ); + //gui.add( twistMinAng.set("Twist Min Ang", -1, -5, 5) ); + //gui.add( twistMaxAng.set("Twist Max Ang", 2.5, -5, 5) ); + + gui.add( materialShininess.set("Material Shininess", 20, 0, 127) ); + gui.add( materialAmbient.set("Material Ambient", ofColor(50,50,50), ofColor(0,0,0,0), ofColor(255,255,255,255)) ); + gui.add( materialSpecular.set("Material Specular", ofColor(255,255,255), ofColor(0,0,0,0), ofColor(255,255,255,255)) ); + gui.add( materialEmissive.set("Material Emmissive", ofColor(0,0,0), ofColor(0,0,0,0), ofColor(255,255,255,255)) ); + + gui.loadFromFile( xmlSettingsPath ); + gui.minimizeAll(); + gui.setPosition( ofGetWidth() - gui.getWidth() - 10, 10 ); + + // Load shaders + particleUpdate.load("Shaders/Particles/GL2/Update"); + particleDraw.load("Shaders/Particles/GL2/DrawInstancedGeometry"); + + // Set how many particles we are going to have, this is based on data texture size + textureSize = 128; + numParticles = textureSize * textureSize; + + // Allocate buffers + ofFbo::Settings fboSettings; + fboSettings.width = textureSize; + fboSettings.height = textureSize; + + // We can create several color buffers for one FBO if we want to store velocity for instance, + // then draw to them simultaneously from a shader using gl_FragData[0], gl_FragData[1], etc. + fboSettings.numColorbuffers = 2; + + fboSettings.useDepth = false; + fboSettings.internalformat = GL_RGBA32F; // Gotta store the data as floats, they won't be clamped to 0..1 + fboSettings.textureTarget = GL_TEXTURE_2D; + fboSettings.wrapModeHorizontal = GL_CLAMP_TO_EDGE; + fboSettings.wrapModeVertical = GL_CLAMP_TO_EDGE; + fboSettings.minFilter = GL_NEAREST; // No interpolation, that would mess up data reads later! + fboSettings.maxFilter = GL_NEAREST; + + ofDisableTextureEdgeHack(); + + particleDataFbo.allocate( fboSettings ); + + ofEnableTextureEdgeHack(); + + // We are going to encode our data into the FBOs like this + // + // Buffer 1: XYZ pos, W age + // Buffer 2: XYZ vel, W not used + // + + // Initialise the starting and static data + ofVec4f* startPositionsAndAge = new ofVec4f[numParticles]; + + int tmpIndex = 0; + for( int y = 0; y < textureSize; y++ ) + { + for( int x = 0; x < textureSize; x++ ) + { + ofVec3f pos(0,0,0); + //ofVec3f pos (MathUtils::randomPointOnSphere() * 0.1); + //pos.set( ofRandom(-1,1), ofRandom(0,2), ofRandom(-1,1) ); + float startAge = ofRandom( particleMaxAge ); // position is not very important, but age is, by setting the lifetime randomly somewhere in the middle we can get a steady stream emitting + + startPositionsAndAge[tmpIndex] = ofVec4f( pos.x, pos.y, pos.z, startAge ); + + tmpIndex++; + } + } + + // Upload it to the source texture + particleDataFbo.source()->getTextureReference(0).loadData( (float*)&startPositionsAndAge[0].x, textureSize, textureSize, GL_RGBA ); + + ofPrimitiveMode primitiveMode = OF_PRIMITIVE_TRIANGLES; // as we'll be drawing ths mesh instanced many times, we need to have many triangles as opposed to one long triangle strip + ofMesh tmpMesh; + + ofConePrimitive cone( 0.1, 0.1, 5, 2, primitiveMode ); + //tmpMesh = cone.getMesh(); + + ofBoxPrimitive box( 0.0015, 0.0015, 0.01 ); // we gotta face in the -Z direction + tmpMesh = box.getMesh(); + + singleParticleMesh.append( tmpMesh ); + singleParticleMesh.setMode( primitiveMode ); +} + +//----------------------------------------------------------------------------------------- +// +void ParticleSystemInstancedGeometryGPU::update( float _time, float _timeStep ) +{ + particleMaterial.setAmbientColor( materialAmbient.get() ); + particleMaterial.setSpecularColor( materialSpecular.get() ); + particleMaterial.setEmissiveColor( materialEmissive.get() ); + particleMaterial.setShininess( materialShininess ); + + updateParticles( _time, _timeStep ); +} + +//----------------------------------------------------------------------------------------- +// +void ParticleSystemInstancedGeometryGPU::draw( ofCamera* _camera ) +{ + drawParticles( &particleDraw, _camera ); +} + +//----------------------------------------------------------------------------------------- +// +void ParticleSystemInstancedGeometryGPU::drawGui() +{ + gui.draw(); +} + +//----------------------------------------------------------------------------------------- +// +void ParticleSystemInstancedGeometryGPU::updateParticles( float _time, float _timeStep ) +{ + ofEnableBlendMode( OF_BLENDMODE_DISABLED ); // Important! We just want to write the data as is to the target fbo + + particleDataFbo.dest()->begin(); + + particleDataFbo.dest()->activateAllDrawBuffers(); // if we have multiple color buffers in our FBO we need this to activate all of them + + particleUpdate.begin(); + + particleUpdate.setUniformTexture( "u_particlePosAndAgeTexture", particleDataFbo.source()->getTextureReference(0), 0 ); + particleUpdate.setUniformTexture( "u_particleVelTexture", particleDataFbo.source()->getTextureReference(1), 1 ); + + particleUpdate.setUniform1f("u_time", _time ); + particleUpdate.setUniform1f("u_timeStep", _timeStep ); + + particleUpdate.setUniform1f("u_particleMaxAge", particleMaxAge ); + + particleUpdate.setUniform1f("u_noisePositionScale", noisePositionScale ); + particleUpdate.setUniform1f("u_noiseTimeScale", noiseTimeScale ); + particleUpdate.setUniform1f("u_noisePersistence", noisePersistence ); + particleUpdate.setUniform1f("u_noiseMagnitude", noiseMagnitude ); + particleUpdate.setUniform3f("u_baseSpeed", baseSpeed.get().x, baseSpeed.get().y, baseSpeed.get().z ); + + particleDataFbo.source()->draw(0,0); + + particleUpdate.end(); + + particleDataFbo.dest()->end(); + + particleDataFbo.swap(); +} + +//----------------------------------------------------------------------------------------- +// +void ParticleSystemInstancedGeometryGPU::drawParticles( ofShader* _shader, ofCamera* _camera ) +{ + ofFloatColor particleStartCol = startColor.get(); + ofFloatColor particleEndCol = endColor.get(); + + ofSetColor( ofColor::white ); + ofEnableBlendMode( OF_BLENDMODE_ALPHA ); + + _shader->begin(); + + _shader->setUniformTexture("u_particlePosAndAgeTexture", particleDataFbo.source()->getTextureReference(0), 1 ); + _shader->setUniformTexture("u_particleVelTexture", particleDataFbo.source()->getTextureReference(1), 2 ); + + _shader->setUniform2f("u_resolution", particleDataFbo.source()->getWidth(), particleDataFbo.source()->getHeight() ); + _shader->setUniform1f("u_time", ofGetElapsedTimef() ); + + _shader->setUniformMatrix4f("u_modelViewMatrix", _camera->getModelViewMatrix() ); + _shader->setUniformMatrix4f("u_projectionMatrix", _camera->getProjectionMatrix() ); + _shader->setUniformMatrix4f("u_modelViewProjectionMatrix", _camera->getModelViewProjectionMatrix() ); + + _shader->setUniform1f("u_particleMaxAge", particleMaxAge ); + + _shader->setUniform1i("u_numLights", 1 ); + + _shader->setUniform4fv("u_particleStartColor", particleStartCol.v ); + _shader->setUniform4fv("u_particleEndColor", particleEndCol.v ); + + // Calling begin() on the material sets the OpenGL state that we then read in the shader + particleMaterial.begin(); + + singleParticleMesh.drawInstanced( OF_MESH_FILL, numParticles ); + + particleMaterial.end(); + + _shader->end(); + +} diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/ParticleSystemInstancedGeometryGPU.h b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/ParticleSystemInstancedGeometryGPU.h new file mode 100644 index 00000000000..6ba267506e2 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/ParticleSystemInstancedGeometryGPU.h @@ -0,0 +1,68 @@ +// +// ParticleSystemGPU.h +// ParticlesGPU +// +// Created by Andreas Müller on 11/01/2015. +// +// + +#pragma once + +#include "ofMain.h" +#include "ofxGui.h" +#include "ofxAutoReloadedShader.h" + +#include "Math/MathUtils.h" +#include "Utils/FboPingPong.h" + +class ParticleSystemInstancedGeometryGPU +{ + + public: + + void init( int _texSize ); + void update( float _time, float _timeStep ); + void draw( ofCamera* _camera ); + + void updateParticles( float _time, float _timeStep ); + void drawParticles( ofShader* _shader, ofCamera* _camera ); + + void drawGui(); + + int numParticles; + int textureSize; + + FboPingPong particleDataFbo; + + ofVboMesh singleParticleMesh; + + ofMaterial particleMaterial; + + ofxAutoReloadedShader particleUpdate; + ofxAutoReloadedShader particleDraw; + + + ofxPanel gui; + ofParameter particleMaxAge; + ofParameter noisePositionScale; + ofParameter noiseMagnitude; + ofParameter noiseTimeScale; + ofParameter noisePersistence; + ofParameter twistNoiseTimeScale; + ofParameter twistNoisePosScale; + ofParameter twistMinAng; + ofParameter twistMaxAng; + + ofParameter baseSpeed; + + ofParameter startColor; + ofParameter endColor; + + //ofParameter materialDiffuse; // We will provide our own diffuse per particle + ofParameter materialAmbient; + ofParameter materialSpecular; + ofParameter materialEmissive; + + ofParameter materialShininess; + +}; diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/Cameras/ofxFirstPersonCamera.h b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/Cameras/ofxFirstPersonCamera.h new file mode 100755 index 00000000000..1a19b9e669f --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/Cameras/ofxFirstPersonCamera.h @@ -0,0 +1,308 @@ +// +// FirstPersonCamera.h +// GaiaTerrain +// +// Created by Andreas Müller on 24/01/2014. +// +// + +#pragma once + +#include "ofMain.h" + +#include "ofAppGLFWWindow.h" + +class ofxFirstPersonCamera : public ofCamera +{ + public: + + // ---------------------------------------------- + ofxFirstPersonCamera() + { + forwardKey = 'w'; + backwardKey = 's'; + rightKey = 'a'; + leftKey = 'd'; + upKey = 'q'; + downKey = 'z'; + + movementMaxSpeed = 0.1f; + movementInertia = 0.15f; + movementDrag = 0.99f; + + mouseSensitivity = 0.3f; + rotationInertia = 0.2f; + rotationDrag = 0.8f; + + lastMouse = ofVec2f( ofGetMouseX(), ofGetMouseY() ); + + prevFrameNumReadJoystick = 0; + + useJoystick = false; + axisRotateX = 3; + axisRotateY = 2; + axisMoveForwards = 1; + axisMoveSideways = 0; + axisDeadZone = 0.15f; + rotationJoystickInertia = 0.4f; + + autoUpdate = false; + enableAutoUpdate(); + } + + // ---------------------------------------------- + ~ofxFirstPersonCamera() + { + disableAutoUpdate(); + } + + // ---------------------------------------------- + void enableAutoUpdate() + { + ofAddListener(ofEvents().update , this, &ofxFirstPersonCamera::update); + autoUpdate = true; + } + + // ---------------------------------------------- + void disableAutoUpdate() + { + if( autoUpdate ) + { + ofRemoveListener(ofEvents().update, this, &ofxFirstPersonCamera::update); + autoUpdate = false; + } + } + + // ---------------------------------------------- + virtual void update() + { + updateRotationMouse(); + updateTranslationKeyboard(); + + if( useJoystick ) + { + updateRotationJoystick(); + updateTranslationJoystick(); + } + } + + // ---------------------------------------------- + void updateRotationMouse( bool _constrainToYAxis = false ) + { + ofVec2f mouse = ofVec2f(ofGetMouseX(), ofGetMouseY()); + ofVec2f mouseVel = mouse - lastMouse; + lastMouse = mouse; + + // Rotation + rotationSpeed *= rotationDrag; + if( rotationSpeed.length() < 0.00000001f ) { rotationSpeed = ofVec3f(0,0,0); } + + if( ofGetMousePressed( OF_MOUSE_BUTTON_RIGHT) ) + { + rotationSpeed.x = ofLerp( rotationSpeed.x, mouseVel.y * mouseSensitivity, rotationInertia ); + rotationSpeed.y = ofLerp( rotationSpeed.y, mouseVel.x * mouseSensitivity, rotationInertia ); + } + + ofQuaternion tmpRotX( rotationSpeed.x, ofVec3f(1,0,0)); + ofQuaternion tmpRotY( rotationSpeed.y, ofVec3f(0,1,0)); + + // Todo: neater solution to this + if( _constrainToYAxis ) { setOrientation( getOrientationQuat() * tmpRotY ); } + else { setOrientation( tmpRotX * getOrientationQuat() * tmpRotY ); } + } + + // ---------------------------------------------- + void updateTranslationKeyboard() + { + // Translation + ofVec3f currPos = getPosition(); + + ofVec3f frameSpeed(0,0,0); // Todo: should we integrate over time as well? + + if( ofGetKeyPressed(forwardKey) ) { frameSpeed -= getZAxis() * movementMaxSpeed; } + if( ofGetKeyPressed(backwardKey) ) { frameSpeed += getZAxis() * movementMaxSpeed; } + + if( ofGetKeyPressed(rightKey) ) { frameSpeed -= getXAxis() * movementMaxSpeed; } + if( ofGetKeyPressed(leftKey) ) { frameSpeed += getXAxis() * movementMaxSpeed; } + + //cout << "getYAxis(): " << getYAxis(); + + if( ofGetKeyPressed(upKey) ) { frameSpeed += getYAxis() * movementMaxSpeed; } + if( ofGetKeyPressed(downKey) ) { frameSpeed -= getYAxis() * movementMaxSpeed; } + + movementSpeed.interpolate( frameSpeed, movementInertia ); + + setPosition( getPosition() + movementSpeed ); + movementSpeed *= movementDrag; + } + + // ---------------------------------------------- + void updateTranslationJoystick() + { + // Translation + ofVec3f currPos = getPosition(); + + ofVec3f frameSpeed(0,0,0); // Todo: should we integrate over time as well? + + float joystickAxisReadingForward = getJoystickAxis( axisMoveForwards, axisDeadZone ); + float joystickAxisReadingSideways = getJoystickAxis( axisMoveSideways, axisDeadZone ); + + frameSpeed -= getZAxis() * -joystickAxisReadingForward * movementMaxSpeed; + frameSpeed -= getXAxis() * -joystickAxisReadingSideways * movementMaxSpeed; + + movementSpeed.interpolate( frameSpeed, movementInertia ); + + setPosition( getPosition() + movementSpeed ); + //movementSpeed *= movementDrag; Todo: we only want to do this once for both + } + + // ---------------------------------------------- + void updateRotationJoystick( bool _constrainToYAxis = false ) + { + float joystickSensitivity = 1.0f; + + // Rotation + rotationJoystickSpeed *= rotationDrag; + if( rotationJoystickSpeed.length() < 0.00000001f ) { rotationJoystickSpeed = ofVec3f(0,0,0); } + + float joystickAxisReadingX = -getJoystickAxis( axisRotateX, axisDeadZone ); + float joystickAxisReadingY = -getJoystickAxis( axisRotateY, axisDeadZone ); + + if( abs(joystickAxisReadingX) > 0.0f ) rotationJoystickSpeed.x = ofLerp( rotationJoystickSpeed.x, joystickAxisReadingX * joystickSensitivity, rotationJoystickInertia ); + if( abs(joystickAxisReadingY) > 0.0f ) rotationJoystickSpeed.y = ofLerp( rotationJoystickSpeed.y, joystickAxisReadingY * joystickSensitivity * 2.0f, rotationJoystickInertia ); + + ofQuaternion tmpRotX( rotationJoystickSpeed.x, ofVec3f(1,0,0)); + ofQuaternion tmpRotY( rotationJoystickSpeed.y, ofVec3f(0,1,0)); + + // Todo: neater solution to this + if( _constrainToYAxis ) { setOrientation( getOrientationQuat() * tmpRotY ); } + else { setOrientation( tmpRotX * getOrientationQuat() * tmpRotY ); } + } + + // ---------------------------------------------- + char setForwardKey( char _forwardKey ) { forwardKey = _forwardKey; } + char setBackwardKey( char _backwardKey) { backwardKey = _backwardKey; } + char setRightKey( char _rightKey) { rightKey = _rightKey; } + char setLeftKey( char _leftKey) { leftKey = _leftKey; } + + // ---------------------------------------------- + char getForwardKey() { return forwardKey; } + char getBackwardKey() { return backwardKey; } + char getRightKey() { return rightKey; } + char getLeftKey() { return leftKey; } + + // ---------------------------------------------- + void setMovementMaxSpeed( float _movementMaxSpeed ) + { + movementMaxSpeed = _movementMaxSpeed; + } + + // ---------------------------------------------- + void setUseJoystick( bool _useJoystick ) + { + useJoystick = _useJoystick; + } + + // ---------------------------------------------- + bool getUseJoystick() + { + return useJoystick; + } + + protected: + + // ---------------------------------------------- + void update(ofEventArgs & args) + { + update(); + } + + // ---------------------------------------------- + float getJoystickAxis( int _axis, float _deadZone = 0.0f ) + { + pollJoystick(); // skips polling if we already polled this frame + + if( _axis < joystickAxis.size() ) + { + float axisReading = joystickAxis.at( _axis ); + int sign = ofSign( axisReading ); + + return ofMap( ofClamp( abs(axisReading) - _deadZone, 0.0f, 1.0f), 0.0f, 1.0f - _deadZone, 0.0f, 1.0f ) * sign; + } + + return 0.0f; + } + + // ---------------------------------------------- + bool isJoystickButtonPressed( int _button ) + { + pollJoystick(); // skips polling if we already polled this frame + + if( _button < joystickButtons.size() ) + { + return joystickButtons.at( _button ); + } + + return false; + } + + // ---------------------------------------------- + void pollJoystick() + { + if( prevFrameNumReadJoystick == ofGetFrameNum() ) + { + return; + } + + int joystickIndex = 0; + if (glfwJoystickPresent( joystickIndex ) == GL_TRUE) + { + int numButtons, numAxes; + const unsigned char *pressed = glfwGetJoystickButtons(joystickIndex, &numButtons); + const float *axes = glfwGetJoystickAxes(joystickIndex, &numAxes); + //printf("%d %d -- %p %p\n", nbuttons, naxes, pressed, axes); + + joystickButtons.clear(); + for( int i = 0; i < numButtons; i++ ) { joystickButtons.push_back( pressed[i] ); } + + joystickAxis.clear(); + for( int i = 0; i < numAxes; i++ ) { joystickAxis.push_back( axes[i] ); } + } + + prevFrameNumReadJoystick = ofGetFrameNum(); + } + + bool useJoystick; + int prevFrameNumReadJoystick; + float axisDeadZone; + vector joystickAxis; + vector joystickButtons; + int axisRotateX; + int axisRotateY; + int axisMoveForwards; + int axisMoveSideways; + float rotationJoystickInertia; + ofVec2f rotationJoystickSpeed; + + + bool autoUpdate; + + float mouseSensitivity; + float rotationInertia; + float rotationDrag; + ofVec2f rotationSpeed; + + float movementMaxSpeed; + float movementInertia; + float movementDrag; + ofVec3f movementSpeed; + + ofVec2f lastMouse; + + char forwardKey; + char backwardKey; + char rightKey; + char leftKey; + char upKey; + char downKey; +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/Cameras/ofxWalkingFirstPersonCamera.h b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/Cameras/ofxWalkingFirstPersonCamera.h new file mode 100755 index 00000000000..bac90be42bf --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/Cameras/ofxWalkingFirstPersonCamera.h @@ -0,0 +1,75 @@ +// +// ofxWalkingFirstPersonCamera.h +// GaiaTerrain +// +// Created by Andreas Müller on 26/01/2014. +// +// + +#pragma once + +#include "ofxFirstPersonCamera.h" + +class ofxWalkingFirstPersonCamera: public ofxFirstPersonCamera +{ + public: + + // ---------------------------------------------- + ofxWalkingFirstPersonCamera() + { + //setGravity( -9.8f ); + setGravity( 0.0f ); + setGroundLevelY( 0.0f ); + setEyeHeight( 0.8f ); + //cout << "ofxWalkingFirstPersonCamera()" << endl; + } + + // ---------------------------------------------- + void update() + { + ofxFirstPersonCamera::update(); + updatePhysics(); + } + + // ---------------------------------------------- + void updatePhysics() + { + ofVec3f currPos = getPosition(); + + // Todo: better physics model + currPos.y += gravity; + + // Todo: base collision check on planes + if( currPos.y < groundLevelY + eyeHeight ) + { + currPos.y = ofLerp( currPos.y, groundLevelY + eyeHeight, 0.5f ); + } + + setPosition( currPos ); + } + + // ---------------------------------------------- + void setGroundLevelY( float _groundLevelY ) + { + groundLevelY = _groundLevelY; + } + + // ---------------------------------------------- + void setEyeHeight( float _eyeHeight ) + { + eyeHeight = _eyeHeight; + } + + // ---------------------------------------------- + void setGravity( float _gravity ) + { + gravity = _gravity; + } + + protected: + + float gravity; + float groundLevelY; + float eyeHeight; + +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/Cameras/ofxWalkingFirstPersonCameraOculus.h b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/Cameras/ofxWalkingFirstPersonCameraOculus.h new file mode 100755 index 00000000000..effcf1d6f8f --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/Cameras/ofxWalkingFirstPersonCameraOculus.h @@ -0,0 +1,83 @@ +// +// ofxWalkingFirstPersonCameraOculus.h +// GaiaTerrain +// +// Created by Andreas Müller on 02/02/2014. +// +// + +#pragma once + +#include "ofxWalkingFirstPersonCamera.h" + +class ofxWalkingFirstPersonCameraOculus : public ofxWalkingFirstPersonCamera +{ + public: + + // ---------------------------------------------- + ofxWalkingFirstPersonCameraOculus() + { + constrainToYAxis = true; + disableAutoUpdate(); + } + + // ---------------------------------------------- + void update() + { + updateRotationMouse( constrainToYAxis ); + updateTranslationBasedOnHeadsetDirection(); + updatePhysics(); + } + + // ---------------------------------------------- + void updateTranslationBasedOnHeadsetDirection() + { + // Translation + ofVec3f currPos = getPosition(); + + ofVec3f forward = ofVec3f(0,0,1) * headsetOrientation * getOrientationQuat(); + ofVec3f sideways = ofVec3f(1,0,0) * headsetOrientation * getOrientationQuat(); + + ofVec3f frameSpeed(0,0,0); // Todo: should we integrate over time as well? + + if( ofGetKeyPressed(forwardKey) ) { frameSpeed -= forward * movementMaxSpeed; } + if( ofGetKeyPressed(backwardKey) ) { frameSpeed += forward * movementMaxSpeed; } + + if( ofGetKeyPressed(rightKey) ) { frameSpeed -= sideways * movementMaxSpeed; } + if( ofGetKeyPressed(leftKey) ) { frameSpeed += sideways * movementMaxSpeed; } + + movementSpeed.interpolate( frameSpeed, movementInertia ); + + setPosition( getPosition() + movementSpeed ); + movementSpeed *= movementDrag; + } + + // ---------------------------------------------- + void getHeadsetOrientation() + { + return headsetOrientation; + } + + // ---------------------------------------------- + void setHeadsetOrientation( ofQuaternion _headsetOrientation ) + { + headsetOrientation = _headsetOrientation; + } + + // ---------------------------------------------- + void setConstrainToYAxis( bool _constrainToYAxis ) + { + constrainToYAxis = _constrainToYAxis; + } + + // ---------------------------------------------- + void getConstrainToYAxis() + { + return constrainToYAxis; + } + + protected: + + ofQuaternion headsetOrientation; + bool constrainToYAxis; +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/DrawingHelpers.h b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/DrawingHelpers.h new file mode 100644 index 00000000000..f369767b96e --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/DrawingHelpers.h @@ -0,0 +1,74 @@ +// +// DrawingHelpers.h +// FirstPersonCamera +// +// Created by Andreas Müller on 22/09/2014. +// +// + +#pragma once + +#include "ofMain.h" + +class DrawingHelpers +{ + public: + + //----------------------------------------------------------------------------------------- + // + static void drawFloorGrid( ofTrueTypeFont* _font ) + { + ofSetColor( ofColor(50) ); + + ofPushMatrix(); + ofRotate(90, 0, 0, -1); + ofDrawGridPlane( 10, 1, false ); + ofPopMatrix(); + + ofDisableDepthTest(); + + ofSetColor( ofColor(100) ); + + float tickDistMm = 1000; + int numTicks = 20; + ofVec2f start( ((numTicks * -0.5) * tickDistMm), ((numTicks * -0.5) * tickDistMm) ); + ofVec2f end( ((numTicks * 0.5) * tickDistMm), ((numTicks * 0.5) * tickDistMm) ); + for( int y = 0; y <= numTicks; y++ ) + { + float tmpY = start.y + (y * tickDistMm); + + ofLine( ofVec3f( start.x, 0, tmpY), ofVec3f( end.x, 0, tmpY) ); + + for( int x = 0; x <= numTicks; x++ ) + { + float tmpX = start.x + (x * tickDistMm); + ofLine( ofVec3f( tmpX, 0, start.y), ofVec3f( tmpX, 0, end.y) ); + } + } + + ofSetColor( ofColor(150) ); + + for( int y = 0; y <= numTicks; y++ ) + { + float tmpY = start.y + (y * tickDistMm); + + ofPushMatrix(); + ofTranslate( ofVec3f(-10,0,tmpY + 10) ); + ofRotateX(-90); + ofScale(2,2,2); + //fontLarge.drawString( ofToString(tmpY), 0, 0 ); + if( _font != NULL ) _font->drawString( ofToString(tmpY), 0, 0 ); + ofPopMatrix(); + + ofPushMatrix(); + ofTranslate( ofVec3f(tmpY + 10,0, -10) ); + ofRotateX(-90); + ofScale(2,2,2); + //fontLarge.drawString( ofToString(tmpY), 0, 0 ); + if( _font != NULL ) _font->drawString( ofToString(tmpY), 0, 0 ); + ofPopMatrix(); + } + + ofEnableDepthTest(); + } +}; diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/FboPingPong.cpp b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/FboPingPong.cpp new file mode 100644 index 00000000000..16b8aee4728 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/FboPingPong.cpp @@ -0,0 +1,115 @@ +// +// FboPingPong.cpp +// emptyExample +// +// Created by Andreas Müller on 12/08/2013. +// +// + +#include "FboPingPong.h" + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::allocate( int _w, int _h, int _internalformat, ofColor _clearColor ) +{ + ofFbo::Settings settings = ofFbo::Settings(); + settings.width = _w; + settings.height = _h; + settings.useDepth = true; + settings.internalformat = _internalformat; + + allocate( settings, _clearColor ); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::allocate( ofFbo::Settings _settings, ofColor _clearColor ) +{ + clearColor = _clearColor; + + fbo1.allocate( _settings); + fbo2.allocate( _settings ); + + sourceBuffer = &fbo1; + destBuffer = &fbo2; + + clearSource(); + clearDest(); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::draw( ofPoint _pos, float _width, bool _drawBack ) +{ + float desWidth = _width; + float desHeight = (source()->getWidth() / source()->getHeight()) * desWidth; + + source()->draw( _pos, desWidth, desHeight ); + + if( _drawBack ) + { + dest()->draw( _pos + ofVec2f(desWidth,0), desWidth, desHeight ); + } +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearBoth() +{ + clearSource(); + clearDest(); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearBoth( ofColor _clearColor ) +{ + clearSource( _clearColor ); + clearDest( _clearColor ); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearSource() +{ + clearSource( clearColor ); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearDest() +{ + clearDest( clearColor ); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearSource( ofColor _clearColor ) +{ + source()->begin(); + ofClear( _clearColor ); + source()->end(); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::clearDest( ofColor _clearColor ) +{ + dest()->begin(); + ofClear( _clearColor ); + dest()->end(); +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::setClearColor( ofColor _color ) +{ + clearColor = _color; +} + +// ------------------------------------------------------------------------------------ +// +void FboPingPong::swap() +{ + std::swap(sourceBuffer, destBuffer); +} \ No newline at end of file diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/FboPingPong.h b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/FboPingPong.h new file mode 100644 index 00000000000..ac001de1f75 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/FboPingPong.h @@ -0,0 +1,47 @@ +// +// FboPingPong.h +// emptyExample +// +// Created by Andreas Müller on 12/08/2013. +// +// + +#pragma once + +#include "ofMain.h" + +class FboPingPong +{ + public: + + void allocate( int _w, int _h, int internalformat = GL_RGB, ofColor _clearColor = ofColor(255,255,255) ); + void allocate( ofFbo::Settings _settings, ofColor _clearColor = ofColor(255,255,255) ); + + ofFbo* source() { return sourceBuffer; } + ofFbo* dest() { return destBuffer; } + + void draw( ofPoint _pos, float _width, bool _drawBack = false ); + + void clearBoth(); + void clearBoth( ofColor _clearColor ); + + void clearSource(); + void clearDest(); + + void clearSource( ofColor _clearColor ); + void clearDest( ofColor _clearColor ); + + void setClearColor( ofColor _color ); + + void swap(); + + private: + + ofFbo* sourceBuffer; + ofFbo* destBuffer; + + ofFbo fbo1; + ofFbo fbo2; + + ofColor clearColor; +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/FileUtils.h b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/FileUtils.h new file mode 100644 index 00000000000..da4f26611f3 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/FileUtils.h @@ -0,0 +1,93 @@ +// +// FileUtils.h +// ParticleMaker +// +// Created by Andreas Müller on 04/03/2014. +// +// + +#pragma once + +class FileUtils +{ + public: + + // --------------------------------------------------------------------------------------------------------------------------------------------------- + // + static vector getFilePathsInFolder( string _folder, string _extension ) + { + vector tmpExt; + tmpExt.push_back( _extension ); + return getFilePathsInFolder( _folder, tmpExt ); + } + + // --------------------------------------------------------------------------------------------------------------------------------------------------- + // + static vector getFilePathsInFolder( string _folder, vector _extensions ) + { + vector filePaths; + + ofDirectory tmpDir( _folder ); + for( unsigned int i = 0; i < _extensions.size(); i++ ) + { + tmpDir.allowExt( _extensions.at(i) ); + } + + int numFiles = tmpDir.listDir( _folder ); + for( int i = 0; i < numFiles; i++ ) + { + filePaths.push_back( tmpDir.getPath(i) ); + } + + return filePaths; + } + + // --------------------------------------------------------------------------------------------------------------------------------------------------- + // + static string getFirstFileOfTypeInFolder( string _folder, string _extension ) + { + vector tmpFiles = getFilePathsInFolder( _folder, _extension ); + if( tmpFiles.size() > 0 ) + { + return tmpFiles.at(0); + } + return ""; + } + + // --------------------------------------------------------------------------------------------------------------------------------------------------- + // + static vector getImageFilePathsInFolder( string _folder ) + { + vector tmpExt; + tmpExt.push_back( "png" ); + tmpExt.push_back( "bmp" ); + tmpExt.push_back( "jpg" ); + tmpExt.push_back( "tga" ); + + return getFilePathsInFolder( _folder, tmpExt ); + } + + // --------------------------------------------------------------------------------------------------------------------------------------------------- + // + static void loadImagesInFolder( string _folder, string _extension, vector& _images ) + { + vector tmpExt; + tmpExt.push_back( _extension ); + return loadImagesInFolder( _folder, tmpExt, _images ); + } + + // --------------------------------------------------------------------------------------------------------------------------------------------------- + // + static void loadImagesInFolder( string _folder, vector _extensions, vector& _images ) + { + vector textureFilePaths = FileUtils::getFilePathsInFolder( _folder, _extensions ); + + for( unsigned int i = 0; i < textureFilePaths.size(); i++ ) + { + ofImage* tmpIm = new ofImage(); + tmpIm->loadImage( textureFilePaths.at(i) ); + _images.push_back( tmpIm ); + } + } + +}; diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/SizeLimitedDeque.h b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/SizeLimitedDeque.h new file mode 100644 index 00000000000..47c8e17dcba --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/SizeLimitedDeque.h @@ -0,0 +1,99 @@ +// +// SizeLimitedDeque.h +// LostSignalTrackingNew +// +// Created by Andreas Müller on 26/08/2014. +// +// + +#pragma once + +#include +using namespace std; + +template +class SizeLimitedDeque +{ + public: + + // ----------------------------------------- + SizeLimitedDeque() + { + maxSize = -1; + } + + // ----------------------------------------- + void pushFront (const T& _val) + { + dat.push_front( _val ); + limitSizeFromBack(); + } + + // ----------------------------------------- + void pushBack (const T& _val) + { + dat.push_back( _val ); + limitSizeFromFront(); + } + + // ----------------------------------------- + T at( int _index ) + { + return dat.at(_index); + } + + // ----------------------------------------- + size_t size() + { + return dat.size(); + } + + // ----------------------------------------- + void setMaxSize( int _size ) + { + maxSize = _size; + } + + + // ----------------------------------------- + int getMaxSize() + { + return maxSize; + } + + deque dat; + + protected: + + // ----------------------------------------- + void limitSizeFromBack() + { + if( maxSize > -1 ) + { + if( dat.size() > maxSize ) + { + while( dat.size() > maxSize ) + { + dat.pop_back(); + } + } + } + } + + // ----------------------------------------- + void limitSizeFromFront() + { + if( maxSize > -1 ) + { + if( dat.size() > maxSize ) + { + while( dat.size() > maxSize ) + { + dat.pop_front(); + } + } + } + } + + int maxSize; +}; \ No newline at end of file diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/ofEasyCamExt.cpp b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/ofEasyCamExt.cpp new file mode 100644 index 00000000000..2ad4dcc8190 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/ofEasyCamExt.cpp @@ -0,0 +1,402 @@ +#include "ofEasyCamExt.h" +#include "ofMath.h" +#include "ofUtils.h" + +//---------------------------------------- +ofEasyCamExt::ofEasyCamExt() +{ + lastTap = 0; + lastDistance = 0; + drag = 0.9f; + + sensitivityRot = 0.01f; //when 1 moving the mouse from one side to the other of the arcball (min(viewport.width, viewport.height)) will rotate 180degrees. when .5, 90 degrees. + sensitivityXY = 0.5; + sensitivityZ = 0.01; + + bDistanceSet = false; + bMouseInputEnabled = false; + bDoRotate = false; + bApplyInertia =false; + bDoTranslate = false; + bInsideArcball = true; + bValidClick = false; + bEnableMouseMiddleButton = true; + bAutoDistance = true; + + doTranslationKey = 0; + doDollyKey = 0; + + dollyForwardKey = 'a'; + dollyBackwardKey = 'z'; + + dollyImpulseAmount = 0.01f; + maxDollyImpulseSpeed = 1.0f; + + reset(); + enableMouseInput(); + +} + +//---------------------------------------- +ofEasyCamExt::~ofEasyCamExt(){ + disableMouseInput(); +} +//---------------------------------------- +void ofEasyCamExt::update(ofEventArgs & args) +{ + + if(!bDistanceSet && bAutoDistance) + { + setDistance(getImagePlaneDistance(viewport), true); + } + + if(bMouseInputEnabled) + { + rotationFactor = sensitivityRot * 180 / min(viewport.width, viewport.height); + if (bMouseInputEnabled) + { + updateMouse(); + } + + if (bDoRotate) + { + updateRotation(); + } + else if (bDoTranslate) + { + updateTranslation(); + } + } + + if( dollyForwardKey != 0 ) + { + if( ofGetKeyPressed(dollyForwardKey) ) { dollyImpulse( -dollyImpulseAmount ); } + } + + if( dollyBackwardKey != 0 ) + { + if( ofGetKeyPressed(dollyBackwardKey) ) { dollyImpulse( dollyImpulseAmount ); } + } + + float minDifference = 0.1e-5; + +// if (bApplyInertia) { + moveX *= drag; + moveY *= drag; + moveZ *= drag; + if (ABS(moveX) <= minDifference && ABS(moveY) <= minDifference && ABS(moveZ) <= minDifference) { + //bApplyInertia = false; + bDoTranslate = false; + } +// } + + move((getXAxis() * moveX) + (getYAxis() * moveY) + (getZAxis() * moveZ)); + + +} +//---------------------------------------- +void ofEasyCamExt::begin(ofRectangle viewport){ + this->viewport = viewport; + ofCamera::begin(viewport); +} + +//---------------------------------------- +void ofEasyCamExt::reset(){ + target.resetTransform(); + + target.setPosition(0,0, 0); + lookAt(target); + + resetTransform(); + setPosition(0, 0, lastDistance); + + + xRot = 0; + yRot = 0; + zRot = 0; + + moveX = 0; + moveY = 0; + moveZ = 0; +} +//---------------------------------------- +void ofEasyCamExt::setTarget(const ofVec3f& targetPoint){ + target.setPosition(targetPoint); + lookAt(target); +} +//---------------------------------------- +void ofEasyCamExt::setTarget(ofNode& targetNode){ + target = targetNode; + lookAt(target); +} +//---------------------------------------- +ofNode& ofEasyCamExt::getTarget(){ + return target; +} +//---------------------------------------- +void ofEasyCamExt::setDistance(float distance){ + setDistance(distance, true); +} +//---------------------------------------- +void ofEasyCamExt::setDistance(float distance, bool save){//should this be the distance from the camera to the target? + if (distance > 0.0f){ + if(save){ + this->lastDistance = distance; + } + setPosition(target.getPosition() + (distance * getZAxis())); + bDistanceSet = true; + } +} +//---------------------------------------- +float ofEasyCamExt::getDistance() const { + return target.getPosition().distance(getPosition()); +} +//---------------------------------------- +void ofEasyCamExt::setAutoDistance(bool bAutoDistance){ + this->bAutoDistance = bAutoDistance; + if (bAutoDistance) { + bDistanceSet = false; + } +} +//---------------------------------------- +void ofEasyCamExt::setDrag(float drag){ + this->drag = drag; +} +//---------------------------------------- +float ofEasyCamExt::getDrag() const { + return drag; +} +//---------------------------------------- +void ofEasyCamExt::setTranslationKey(char key){ + doTranslationKey = key; +} +//---------------------------------------- +char ofEasyCamExt::getTranslationKey(){ + return doTranslationKey; +} + +//---------------------------------------- +void ofEasyCamExt::setDollyKey(char _key) { + doDollyKey = _key; +} + +//---------------------------------------- +char ofEasyCamExt::getDollyKey() { + return doDollyKey; +} + +//---------------------------------------- +void ofEasyCamExt::setDollyForwardKey(char _key) +{ + dollyForwardKey = _key; +} + +//---------------------------------------- +char ofEasyCamExt::getDollyForwardKey() +{ + return dollyForwardKey; +} + +//---------------------------------------- +void ofEasyCamExt::setDollyBackwardKey(char _key) +{ + dollyBackwardKey = _key; +} + +//---------------------------------------- +char ofEasyCamExt::getDollyBackwardKey() +{ + return dollyBackwardKey; +} + +//---------------------------------------- +void ofEasyCamExt::setDollyImpulseMagnitude( float _impulseMagnitude ) +{ + dollyImpulseAmount = _impulseMagnitude; + maxDollyImpulseSpeed = dollyImpulseAmount * 4.0f; +} + +//---------------------------------------- +float ofEasyCamExt::getDollyImpulseMagnitude() +{ + return dollyImpulseAmount; +} + +//---------------------------------------- +void ofEasyCamExt::dollyImpulse( float _impulse ){ + + //cout << "dollyImpulse: " << _impulse << endl;; + + moveZ += _impulse; + moveZ = ofClamp( moveZ, -maxDollyImpulseSpeed, maxDollyImpulseSpeed ); +} + +//---------------------------------------- +void ofEasyCamExt::enableMouseInput(){ + if(!bMouseInputEnabled){ + bMouseInputEnabled = true; + ofAddListener(ofEvents().update , this, &ofEasyCamExt::update); + } +} +//---------------------------------------- +void ofEasyCamExt::disableMouseInput(){ + if(bMouseInputEnabled){ + bMouseInputEnabled = false; + ofRemoveListener(ofEvents().update, this, &ofEasyCamExt::update); + } +} + +//---------------------------------------- +bool ofEasyCamExt::getMouseInputEnabled(){ + return bMouseInputEnabled; +} + +//---------------------------------------- +void ofEasyCamExt::enableMouseMiddleButton(){ + bEnableMouseMiddleButton = true; +} + +//---------------------------------------- +void ofEasyCamExt::disableMouseMiddleButton(){ + bEnableMouseMiddleButton = false; +} + +//---------------------------------------- +bool ofEasyCamExt::getMouseMiddleButtonEnabled(){ + return bEnableMouseMiddleButton; +} + +//---------------------------------------- +void ofEasyCamExt::updateTranslation() +{ + move((getXAxis() * moveX) + (getYAxis() * moveY) + (getZAxis() * moveZ)); +} + +//---------------------------------------- +void ofEasyCamExt::updateRotation(){ + if (bApplyInertia) { + xRot *=drag; + yRot *=drag; + zRot *=drag; + + float minDifference = 0.1e-5; + + if (ABS(xRot) <= minDifference && ABS(yRot) <= minDifference && ABS(zRot) <= minDifference) { + bApplyInertia = false; + bDoRotate = false; + } + } + curRot = ofQuaternion(xRot, ofCamera::getXAxis(), yRot, ofCamera::getYAxis(), zRot, ofCamera::getZAxis()); + setPosition((ofCamera::getGlobalPosition()-target.getGlobalPosition())*curRot +target.getGlobalPosition()); + rotate(curRot); +} +//---------------------------------------- +void ofEasyCamExt::updateMouse(){ + + + mouse = ofVec2f(ofGetMouseX(), ofGetMouseY()); + + if(viewport.inside(mouse.x, mouse.y) && !bValidClick && (ofGetMousePressed(OF_MOUSE_BUTTON_RIGHT) || ofGetMousePressed(OF_MOUSE_BUTTON_MIDDLE)) ) + { + unsigned long doubleclickTime = 200; + unsigned long curTap = ofGetElapsedTimeMillis(); + if(lastTap != 0 && curTap - lastTap < doubleclickTime) + { + reset(); + } + + if ((bEnableMouseMiddleButton && ofGetMousePressed(OF_MOUSE_BUTTON_MIDDLE)) || + ofGetKeyPressed(doTranslationKey) || + ofGetKeyPressed(doDollyKey) /*|| ofGetMousePressed(OF_MOUSE_BUTTON_RIGHT)*/ ) + { + bDoTranslate = true; + bDoRotate = false; + bApplyInertia = false; + } + else if (ofGetMousePressed(OF_MOUSE_BUTTON_RIGHT) ) + { + bDoTranslate = false; + bDoRotate = true; + bApplyInertia = false; + if(ofVec2f(mouse.x - viewport.x - (viewport.width/2), mouse.y - viewport.y - (viewport.height/2)).length() < min(viewport.width/2, viewport.height/2)) + { + bInsideArcball = true; + } + else + { + bInsideArcball = false; + } + } + + lastTap = curTap; + //lastMouse = ofVec2f(ofGetPreviousMouseX(),ofGetPreviousMouseY()); //this was causing the camera to have a tiny "random" rotation when clicked. + lastMouse = mouse; + bValidClick = true; + bApplyInertia = false; + } + + + if (bValidClick) + { + if ( !(ofGetMousePressed(OF_MOUSE_BUTTON_RIGHT) || ofGetMousePressed(OF_MOUSE_BUTTON_MIDDLE)) ) + { + bApplyInertia = true; + bValidClick = false; + } + else + { + int vFlip; + if(isVFlipped()){ + vFlip = -1; + }else{ + vFlip = 1; + } + + mouseVel = mouse - lastMouse; + + if (bDoTranslate) + { + moveX = 0; + moveY = 0; + //moveZ = 0; + + //if ( ofGetMousePressed(OF_MOUSE_BUTTON_RIGHT)) + if ( ofGetKeyPressed(doDollyKey) ) + { + //moveZ = mouseVel.y * sensitivityZ * (getDistance() + FLT_EPSILON)/ viewport.height; + moveZ += mouseVel.y * sensitivityZ * (getDistance() + FLT_EPSILON)/ viewport.height; + } + else + { + moveX = -mouseVel.x * sensitivityXY * (getDistance() + FLT_EPSILON)/viewport.width; + moveY = vFlip * mouseVel.y * sensitivityXY * (getDistance() + FLT_EPSILON)/viewport.height; + } + } + else + { + //xRot = 0; + //yRot = 0; + //zRot = 0; + + if (bInsideArcball) + { + //xRot = vFlip * -mouseVel.y * rotationFactor; + //yRot = -mouseVel.x * rotationFactor; + + xRot += vFlip * -mouseVel.y * rotationFactor; + yRot += -mouseVel.x * rotationFactor; + } + else + { + ofVec2f center(viewport.width/2, viewport.height/2); + //zRot = -vFlip * ofVec2f(mouse.x - viewport.x - center.x, mouse.y - viewport.y - center.y).angle(lastMouse - ofVec2f(viewport.x, viewport.y) - center); + zRot += (-vFlip * ofVec2f(mouse.x - viewport.x - center.x, mouse.y - viewport.y - center.y).angle(lastMouse - ofVec2f(viewport.x, viewport.y) - center)) * sensitivityRot; + } + } + lastMouse = mouse; + } + } + +} + + diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/ofEasyCamExt.h b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/ofEasyCamExt.h new file mode 100644 index 00000000000..546a12bda5d --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/ofEasyCamExt.h @@ -0,0 +1,122 @@ +#pragma once + +#include "ofParameter.h" +#include "ofCamera.h" +#include "ofEvents.h" + +/* + 99% Memo's ofEasyCam, I just needed to change a few small things and was having trouvle doing it by subclassing. + */ + +class ofEasyCamExt : public ofCamera +{ + public: + + ofEasyCamExt(); + ~ofEasyCamExt(); + + // TODO: this should be ofGetViewRect() eventually + virtual void begin(ofRectangle viewport = ofGetCurrentViewport()); + void reset(); + + //---------------------------------------- + // advanced functions + + void setTarget(const ofVec3f& target); + void setTarget(ofNode& target); + ofNode& getTarget(); + + void setDistance(float distance); + float getDistance() const; + + // drag is how quickly the camera picks up and slows down + // it is a normalized value between 0-1 + void setDrag(float drag); + float getDrag() const; + // the translation key is the key used to switch between rotation and translation. + // translation happens only when the key is pressed. + void setTranslationKey(char key); + char getTranslationKey(); + + void setDollyKey(char _key); + char getDollyKey(); + + void setDollyForwardKey(char _key); + char getDollyForwardKey(); + + void setDollyBackwardKey(char _key); + char getDollyBackwardKey(); + + void setDollyImpulseMagnitude( float _impulseMagnitude ); + float getDollyImpulseMagnitude(); + + void dollyImpulse( float _impulse ); + + // enable or disable mouse input to navigate + void enableMouseInput(); + void disableMouseInput(); + bool getMouseInputEnabled(); + + void enableMouseMiddleButton(); + void disableMouseMiddleButton(); + bool getMouseMiddleButtonEnabled(); + + void setAutoDistance(bool bAutoDistance); + + protected: + + void setDistance(float distance, bool save); + + ofNode target; + + bool bEnableMouseMiddleButton; + bool bApplyInertia; + bool bDoTranslate; + bool bDoRotate; + bool bValidClick; + bool bInsideArcball; + bool bMouseInputEnabled; + bool bDistanceSet; + bool bAutoDistance; + float lastDistance; + + float drag; + + float xRot; + float yRot; + float zRot; + + float moveX; + float moveY; + float moveZ; + + float sensitivityXY; + float sensitivityZ; + float sensitivityRot; + + float rotationFactor; + + ofVec2f mouse; + ofVec2f lastMouse; + ofVec2f mouseVel; + + void updateRotation(); + void updateTranslation(); + void update(ofEventArgs & args); + void updateMouse(); + + char doTranslationKey; + char doDollyKey; + + char dollyForwardKey; + char dollyBackwardKey; + + float dollyImpulseAmount; + float maxDollyImpulseSpeed; + + unsigned long lastTap; + + ofQuaternion curRot; + + ofRectangle viewport;// having the viewport saved localy will make it easier for all the needed maths dealing with viewport. +}; diff --git a/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/ofTrueTypeFontExt.h b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/ofTrueTypeFontExt.h new file mode 100644 index 00000000000..f6c16734735 --- /dev/null +++ b/workshops/NoiseWorkshop/ParticleSystemInstancedGPU/src/Utils/ofTrueTypeFontExt.h @@ -0,0 +1,40 @@ +#pragma once + +#include "ofMain.h" + +class ofTrueTypeFontExt : public ofTrueTypeFont +{ + public: + + // ----------------------------------------------------------------- + void drawStringShadowed( string _s, ofVec2f _pos, + ofColor _frontColor = ofColor(255,255,255), ofColor _backColor = ofColor(0,0,0) ) + { + drawStringShadowed( _s, _pos.x, _pos.y, _frontColor, _backColor ); + } + + // ----------------------------------------------------------------- + void drawStringShadowed( string _s, float _x, float _y, + ofColor _frontColor = ofColor(255,255,255), ofColor _backColor = ofColor(0,0,0) ) + { + ofSetColor( _backColor ); + drawString( _s, _x + 1, _y + 1 ); + + ofSetColor( _frontColor ); + drawString( _s, _x, _y ); + } + + // ----------------------------------------------------------------- + void drawTextureAtlas( float _x, float _y, float _w, float _h ) + { + + if( _w == 0.0f || _h == 0.0f ) + { + _w = texAtlas.getWidth(); + _h = texAtlas.getHeight(); + } + + texAtlas.draw( _x, _y, _w, _h ); + } + +};