From 88c01266abd1c9190e34e6a7a1d26322c2f6ab8b Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 25 Jun 2014 03:29:08 -0700 Subject: [PATCH] =?UTF-8?q?more=20hair=20tweaks,=20don=E2=80=99t=20render?= =?UTF-8?q?=20in=201P,=20improved=20toy=20ball?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/toyball.js | 41 +++++++++++++++++++++---------- interface/src/avatar/Avatar.cpp | 39 +++++++++++++++-------------- interface/src/avatar/Avatar.h | 1 + interface/src/avatar/MyAvatar.cpp | 2 +- 4 files changed, 50 insertions(+), 33 deletions(-) diff --git a/examples/toyball.js b/examples/toyball.js index d312c1bc94..5fd522ef4a 100644 --- a/examples/toyball.js +++ b/examples/toyball.js @@ -26,14 +26,21 @@ var RIGHT_TIP = 3; var RIGHT_BUTTON_FWD = 11; var RIGHT_BUTTON_3 = 9; +var BALL_RADIUS = 0.08; +var GRAVITY_STRENGTH = 0.5; + +var HELD_COLOR = { red: 240, green: 0, blue: 0 }; +var THROWN_COLOR = { red: 128, green: 0, blue: 0 }; + var leftBallAlreadyInHand = false; var rightBallAlreadyInHand = false; var leftHandParticle; var rightHandParticle; -var throwSound = new Sound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw"); +var newSound = new Sound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw"); var catchSound = new Sound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/catch.raw"); -var targetRadius = 0.25; +var throwSound = new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Switches%20and%20sliders/slider%20-%20whoosh1.raw"); +var targetRadius = 1.0; var wantDebugging = false; @@ -54,7 +61,7 @@ function getBallHoldPosition(whichSide) { tipPosition = Controller.getSpatialControlPosition(RIGHT_TIP); } - var BALL_FORWARD_OFFSET = 0.08; // put the ball a bit forward of fingers + var BALL_FORWARD_OFFSET = 0.15; // put the ball a bit forward of fingers position = { x: BALL_FORWARD_OFFSET * normal.x, y: BALL_FORWARD_OFFSET * normal.y, z: BALL_FORWARD_OFFSET * normal.z }; @@ -69,6 +76,7 @@ function getBallHoldPosition(whichSide) { function checkControllerSide(whichSide) { var BUTTON_FWD; var BUTTON_3; + var TRIGGER; var palmPosition; var ballAlreadyInHand; var handMessage; @@ -76,18 +84,20 @@ function checkControllerSide(whichSide) { if (whichSide == LEFT_PALM) { BUTTON_FWD = LEFT_BUTTON_FWD; BUTTON_3 = LEFT_BUTTON_3; + TRIGGER = 0; palmPosition = Controller.getSpatialControlPosition(LEFT_PALM); ballAlreadyInHand = leftBallAlreadyInHand; handMessage = "LEFT"; } else { BUTTON_FWD = RIGHT_BUTTON_FWD; BUTTON_3 = RIGHT_BUTTON_3; + TRIGGER = 1; palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM); ballAlreadyInHand = rightBallAlreadyInHand; handMessage = "RIGHT"; } - - var grabButtonPressed = (Controller.isButtonPressed(BUTTON_FWD) || Controller.isButtonPressed(BUTTON_3)); + + var grabButtonPressed = (Controller.isButtonPressed(BUTTON_FWD) || Controller.isButtonPressed(BUTTON_3) || (Controller.getTriggerValue(TRIGGER) > 0.5)); // If I don't currently have a ball in my hand, then try to catch closest one if (!ballAlreadyInHand && grabButtonPressed) { @@ -107,8 +117,11 @@ function checkControllerSide(whichSide) { var ballPosition = getBallHoldPosition(whichSide); var properties = { position: { x: ballPosition.x, y: ballPosition.y, - z: ballPosition.z }, - velocity : { x: 0, y: 0, z: 0}, inHand: true }; + z: ballPosition.z }, + color: HELD_COLOR, + velocity : { x: 0, y: 0, z: 0}, + lifetime : 600, + inHand: true }; Particles.editParticle(closestParticle, properties); var options = new AudioInjectionOptions(); @@ -127,7 +140,7 @@ function checkControllerSide(whichSide) { //} // If '3' is pressed, and not holding a ball, make a new one - if (Controller.isButtonPressed(BUTTON_3) && !ballAlreadyInHand) { + if (grabButtonPressed && !ballAlreadyInHand) { var ballPosition = getBallHoldPosition(whichSide); var properties = { position: { x: ballPosition.x, y: ballPosition.y, @@ -135,11 +148,11 @@ function checkControllerSide(whichSide) { velocity: { x: 0, y: 0, z: 0}, gravity: { x: 0, y: 0, z: 0}, inHand: true, - radius: 0.05, + radius: BALL_RADIUS, damping: 0.999, - color: { red: 255, green: 0, blue: 0 }, + color: HELD_COLOR, - lifetime: 10 // 10 seconds - same as default, not needed but here as an example + lifetime: 600 // 10 seconds - same as default, not needed but here as an example }; newParticle = Particles.addParticle(properties); @@ -155,7 +168,7 @@ function checkControllerSide(whichSide) { var options = new AudioInjectionOptions(); options.position = ballPosition; options.volume = 1.0; - Audio.playSound(catchSound, options); + Audio.playSound(newSound, options); return; // exit early } @@ -188,7 +201,9 @@ function checkControllerSide(whichSide) { y: tipVelocity.y * THROWN_VELOCITY_SCALING, z: tipVelocity.z * THROWN_VELOCITY_SCALING } , inHand: false, - gravity: { x: 0, y: -2, z: 0}, + color: THROWN_COLOR, + lifetime: 10, + gravity: { x: 0, y: -GRAVITY_STRENGTH, z: 0}, }; Particles.editParticle(handParticle, properties); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 2f4717dd3b..ee65b9c170 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -379,22 +379,22 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) { renderHair(); } -const float HAIR_LENGTH = 0.4f; +const float HAIR_LENGTH = 0.2f; const float HAIR_LINK_LENGTH = HAIR_LENGTH / HAIR_LINKS; const float HAIR_DAMPING = 0.99f; -const float HEAD_RADIUS = 0.25f; +const float HEAD_RADIUS = 0.15f; const float COLLISION_RELAXATION = 10.f; const float CONSTRAINT_RELAXATION = 10.0f; -const float NOISE = 0.0f; // 0.1f; -const float NOISE_MAGNITUDE = 0.02f; -const glm::vec3 HAIR_GRAVITY(0.f, -0.05f, 0.f); +const glm::vec3 HAIR_GRAVITY(0.f, -0.005f, 0.f); const float HAIR_ACCELERATION_COUPLING = 0.025f; -const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.15f; -const float HAIR_MAX_LINEAR_ACCELERATION = 5.f; +const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; +const float HAIR_MAX_LINEAR_ACCELERATION = 4.f; const float HAIR_THICKNESS = 0.025f; +const float HAIR_STIFFNESS = 0.002f; const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); -const glm::vec3 WIND_DIRECTION(1.0f, -1.0f, 0.f); +const glm::vec3 WIND_DIRECTION(0.5f, -1.0f, 0.f); +const float MAX_WIND_STRENGTH = 0.01f; void Avatar::renderHair() { // @@ -447,18 +447,14 @@ void Avatar::simulateHair(float deltaTime) { acceleration = acceleration * rotation; glm::vec3 angularVelocity = getAngularVelocity() + getHead()->getAngularVelocity(); - float windIntensity = randFloat() * 0.02f; + float windIntensity = randFloat() * MAX_WIND_STRENGTH; for (int strand = 0; strand < HAIR_STRANDS; strand++) { for (int link = 0; link < HAIR_LINKS; link++) { int vertexIndex = strand * HAIR_LINKS + link; if (vertexIndex % HAIR_LINKS == 0) { // Base Joint - no integration - if (randFloat() < NOISE) { - // Move base of hair - _hairPosition[vertexIndex] += randVector() * NOISE_MAGNITUDE; - } - } else { + } else { // // Vertlet Integration // @@ -477,6 +473,10 @@ void Avatar::simulateHair(float deltaTime) { // Add linear acceleration of the avatar body _hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime; + // Add stiffness (product) + _hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex]) + * powf(1.f - link / HAIR_LINKS, 2.f) * HAIR_STIFFNESS; + // Add some wind glm::vec3 wind = WIND_DIRECTION * windIntensity; _hairPosition[vertexIndex] += wind * deltaTime; @@ -529,17 +529,17 @@ void Avatar::simulateHair(float deltaTime) { } void Avatar::initializeHair() { - const float FACE_WIDTH = 0.25f * PI; + const float FACE_WIDTH = PI / 4.0f; glm::vec3 thisVertex; for (int strand = 0; strand < HAIR_STRANDS; strand++) { float strandAngle = randFloat() * PI; - float azimuth = randFloat() * 2.f * PI; - float elevation; - if ((azimuth > FACE_WIDTH) || (azimuth < -FACE_WIDTH)) { + float azimuth = FACE_WIDTH / 2.0f + (randFloat() * (2.0 * PI - FACE_WIDTH)); + float elevation = PI_OVER_TWO - (randFloat() * 0.75 * PI); + /*if ((azimuth > FACE_WIDTH) || (azimuth < -FACE_WIDTH)) { elevation = randFloat() * PI_OVER_TWO; } else { elevation = (PI_OVER_TWO / 2.f) + randFloat() * (PI_OVER_TWO / 2.f); - } + }*/ glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation)); thisStrand *= HEAD_RADIUS + 0.01f; @@ -562,6 +562,7 @@ void Avatar::initializeHair() { } _hairPosition[vertexIndex] = thisVertex; _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex]; + _hairOriginalPosition[vertexIndex] = _hairPosition[vertexIndex]; _hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * HAIR_THICKNESS, 0.f, sin(strandAngle) * HAIR_THICKNESS); _hairNormals[vertexIndex] = glm::normalize(randVector()); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index bb38cab5c5..40f92b468c 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -201,6 +201,7 @@ protected: virtual void updateJointMappings(); glm::vec3 _hairPosition[HAIR_STRANDS * HAIR_LINKS]; + glm::vec3 _hairOriginalPosition[HAIR_STRANDS * HAIR_LINKS]; glm::vec3 _hairLastPosition[HAIR_STRANDS * HAIR_LINKS]; glm::vec3 _hairQuadDelta[HAIR_STRANDS * HAIR_LINKS]; glm::vec3 _hairNormals[HAIR_STRANDS * HAIR_LINKS]; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6938e20313..bbdf041341 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -833,9 +833,9 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) { // Render head so long as the camera isn't inside it if (shouldRenderHead(Application::getInstance()->getCamera()->getPosition(), renderMode)) { getHead()->render(1.0f, modelRenderMode); + renderHair(); } getHand()->render(true, modelRenderMode); - renderHair(); } const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f;