#VRML V2.0 utf8 # # bounce1.wrl # Bouncing beachball (JavaScript/VRMLscript version) # by David R. Nadeau # # This world illustrates the use of a Script node to create a computed # animation path. In particular, the Script node uses a JavaScript # (or VRMLScript) program script to compute translation values for a # vertically bouncing beach ball. # # The bounce path is based upon the projectile motion equation of # physics, constrained to create a cyclic bouncing path with a # user-selected maximum bounce height. Also, there is no friction, # drag, or damping. # # The equation is derived as follows: # # (1) Projectile motion computes a y(t) value (height as a function # of time) based upon the gravitation constant, g, an initial # y-direction velocity, v0, an initial y position, y0, and the # current time, t: # # y(t) = 0.5 * g * t * t + v0 * t + y0 # # (2) At time t=0, the ball should be on the ground with y=0. # So, y0 = y(0) = 0. The equation in (1) simplifies to: # # y(t) = 0.5 * g * t * t + v0 * t # # (3) At time t=1, at the end of the TimeSensor's fractional time # cycle, the ball should again be on the ground with y=0. # So, y(1) = 0. Plugging this in to the equation in (2), # we get: # # y(t) = 0.5 * g * t * t + v0 * t # y(1) = 0.5 * g * 1 * 1 + v0 * 1 # 0 = 0.5 * g + v0 # # So # # v0 = -0.5 * g # # (4) At time t=0.5, the ball should be at the peak of its bounce # at a user-selected maximum height, h. So, y(0.5) = h. # Plugging this in to the equation in (2), we get: # # y(t) = 0.5 * g * t * t + v0 * t # y(0.5) = 0.5 * g * 0.5 * 0.5 + v0 * 0.5 # h = g * 0.125 + v0 * 0.5 # # And v0 = -0.5 * g from equation (3), so # # h = g * 0.125 - 0.5 * g * 0.5 # h = -g * 0.125 # # So # # g = -8.0 * h # # (5) We can now simplify the equation in (2) using the results # from (3) and (4) to get an equation that computes the # ball height y(t) parameterized only by the maximum height, h, # giving us: # # y(t) = 0.5 * g * t * t + v0 * t # = 0.5 * (-8.0 * h) * t * t + (-0.5 * g) * t # = 0.5 * (-8.0 * h) * t * t + (4.0 * h) * t # = 4.0 * h * (-t * t + t) # = 4.0 * h * t * (1.0 - t) # # In the program script, the maximum height, h, is given in # the 'bounceHeight' field. The current time, t, is given in # the 'set_fraction' eventIn and passed to the eventIn function # as the 'frac' parameter. Using these names, the above # equation becomes: # # y = 4.0 * bounceHeight * frac * (1.0 - frac) # # Things to experiment with # - Encapsulate the ball, script, timer, and sensors within a # PROTO for a new node named "BouncingBall". Then use that # new BouncingBall node multiple times to create multiple # bouncing balls. Your PROTO interface might look like this: # # PROTO BouncingBall [ # field SFFloat bounceHeight 2.0 # field SFTime cycleInterval 2.0 # ] { . . . } # # See 'bounce3.wrl', which implements such a PROTO. # # - Add a shadow under the bouncing ball. To do this, add a # circular, semi-transparent, black shape that doesn't bounce. # To make the shadow more realistic, scale the shadow in the X # and Z directions, shrinking it as the ball goes up, and # increasing it as the ball comes down. You'll need to add # another eventOut for the Script node and send an XYZ scaling # factor triple out that eventOut. Try the following values # for the XYZ scale values: # # xzscale = 1.0 - 0.5 * y / bounceHeight; # shadowScale_changed[0] = xzscale; # shadowScale_changed[1] = 1.0; # shadowScale_changed[2] = xzscale; # # See 'bounce4.wrl', which implements shadows using the above # scale values. # # - Add a sound to the PROTO so that each time the ball touches # the ground, it makes a 'boing' sound. # # - When the ball hits the ground, scale the ball slightly so that # it appears to squish. # WorldInfo { title "Bouncing beachball (JavaScript)" info [ "Copyright (c) 1997, David R. Nadeau" ] } Viewpoint { position 0.0 0.6 8.0 orientation 1.0 0.0 0.0 0.1 } NavigationInfo { type [ "WALK", "ANY" ] headlight FALSE speed 2.0 } DirectionalLight { ambientIntensity 0.5 direction 0.0 -1.0 -0.5 } # # Sky # Background { skyColor [ 0.0 0.0 1.0, 0.0 0.5 1.0, 0.7 0.7 1.0, ] skyAngle [ 1.371, 1.571, ] } # # Beach # Shape { appearance Appearance { material Material { } texture ImageTexture { url "sand.jpg" } textureTransform TextureTransform { scale 10.0 10.0 } } geometry IndexedFaceSet { coord Coordinate { point [ -50.0 -1.0 50.0, 50.0 -1.0 50.0, 50.0 -1.0 -50.0, -50.0 -1.0 -50.0, ] } coordIndex [ 0, 1, 2, 3 ] solid FALSE } } # # Palm trees # Transform { translation -3.0 -1.0 -10.0 children [ DEF Palm Group { children [ # Palm tree - in a billboard so it is never edge-on Billboard { children [ Shape { appearance Appearance { material NULL # emissive texturing texture ImageTexture { url "palm.png" } } geometry IndexedFaceSet { coord Coordinate { point [ -2.5 0.0 0.0, 2.5 0.0 0.0, 2.5 11.25 0.0, -2.5 11.25 0.0, ] } coordIndex [ 0, 1, 2, 3 ] texCoord TextureCoordinate { point [ 0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, ] } texCoordIndex [ 0, 1, 2, 3 ] solid FALSE } } ] } # Fake tree shadow - a black semi-transparent rectangle with # a texture map to give it the right shape Shape { appearance Appearance { material Material { diffuseColor 0.0 0.0 0.0 transparency 0.5 } texture ImageTexture { url "palmsh.png" } } geometry IndexedFaceSet { coord Coordinate { point [ -2.5 0.05 2.5, 2.5 0.05 2.5, 2.5 0.05 -2.5, -2.5 0.05 -2.5, ] } coordIndex [ 0, 1, 2, 3 ] texCoord TextureCoordinate { point [ 0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, ] } texCoordIndex [ 0, 1, 2, 3 ] solid FALSE } } ] } ] } Transform { translation -5.0 -1.0 -6.0 scale 0.6 0.6 0.6 children USE Palm } Transform { translation 5.0 -1.0 -9.0 children USE Palm } Transform { translation 10.0 -1.0 -15.0 children USE Palm } # # Bouncing beach ball # DEF Ball Transform { # animated translation children [ Shape { appearance Appearance { material Material { ambientIntensity 0.5 diffuseColor 1.0 1.0 1.0 specularColor 0.7 0.7 0.7 shininess 0.4 } texture ImageTexture { url "beach.jpg" } textureTransform TextureTransform { scale 2.0 1.0 } } geometry Sphere { } } ] } DEF Clock TimeSensor { cycleInterval 2.0 startTime 1.0 stopTime 0.0 loop TRUE } DEF Bouncer Script { field SFFloat bounceHeight 3.0 eventIn SFFloat set_fraction eventOut SFVec3f value_changed # change 'vrmlscript' to 'javascript' for newer browsers url "vrmlscript: function set_fraction( frac, tm ) { y = 4.0 * bounceHeight * frac * (1.0 - frac); value_changed[0] = 0.0; value_changed[1] = y; value_changed[2] = 0.0; }" } ROUTE Clock.fraction_changed TO Bouncer.set_fraction ROUTE Bouncer.value_changed TO Ball.set_translation