26
Animating a Growing Pile of Sand By Karlee Stein Based on research by Hui Gong and Howard Hamilton

Animating a Growing Pile of Sand By Karlee Stein Based on research by Hui Gong and Howard Hamilton

Embed Size (px)

Citation preview

Animating a Growing Pile of SandBy Karlee Stein

Based on research by Hui Gong and Howard Hamilton

Idea

Change the height of the terrain (height map) as the sand falls If particles falling from a single point

Form a cone-shaped pile that has a height h and a slope θ

θ

h

Steps

1. Distribute added sand to each of the 4 poles surrounding the point where the sand is dropped

2. For each of these poles, check the heights of neighbouring poles to see if the sand should remain there or be distributed to a neighbour

3. If the sand should be distributed, calculate how much should be moved and move it

AddSand() Inputs:

(x, z) where sand is falling

float colSize = WORLD_WIDTH / cols;float rowSize = WORLD_HEIGHT / rows;

float i = (position.x + WORLD_WIDTH/2) / colSize;float k = (position.z + WORLD_HEIGHT/2) / rowSize;

AddSand()

// heightmap indicesfloat qi = floor(i);float qk = floor(k);

// fractional portionfloat fi = i - qi;float fk = k - qk;

AddSand() - Example

(qi, qk) => (1 – fi) * (1 – fk) * height

(qi + 1, qk) => fi * (1 – fk) * height

(qi + 1, qk + 1) => fi * fk * height

(qi, qk + 1) => (1 – fi) * fk * height

1 - fi = 0.8

1 - fk = 0.7

fk = 0.3

fi = 0.2

(qi, qk)

(qi + 1, qk + 1)

(qi + 1, qk)

(qi, qk + 1)

AddSand()

int x = qi;int y = qk;float heightToAdd = (1 - fi) * (1 - fk) * height;float newHeight = h[y][x] + heightToAdd;disperseSand(x, y, newHeight);

x = qi + 1;y = qk;heightToAdd = fi * (1 - fk) * height;newHeight = h[y][x] + heightToAdd;disperseSand(x, y, newHeight);…

ConstantsWORLD_WIDTH = 1000CRITICAL_SLOPE_ANGLE = (30.0 / 180.0 * PI) // θ in radians// HORIZONTAL_CONVERSION is the conversion factor used to convert // between world (terrain) coordinates (in say meters) and height map array // indices. We assume that this conversion factor is the same for both // horizontal dimensions HORIZONTAL_CONVERSION = WORLD_WIDTH / (heightmap.Width – 1);SLOPE = tan(CRITICAL_SLOPE_ANGLE) * HORIZONTAL_CONVERSION

opp

adjθ

Tanθ = opp/adj = SLOPE

Neighbours to Consider

To form a round pile, we examine all grid points in a circle of radius r from the current pole at x, y

The most appropriate value for r depends on the height map resolution and the detail we want to achieve

r = 4

x, y

DistributeSand()

Inputs: (x, y) – point of the pole to add height to Height – new height of the pole (old height + height to add)

Initialize neighbor at which maximum offset height occurs bestNeighbor.x = x; // dummy value bestNeighbor.y = y; // dummy value bestNeighbor.offset = 0;

DistributeSand() – Check Neighbor Heightsny = floor(r);

// process all points across length of circlefor (j = y - ny; j <= y + ny; j++){

nx = floor( sqrt(r*r - (j-y)*(j-y)) );

// process all points across the width of circle// at this y level of the circlefor (i = x – nx; i <= x + nx; i++){

y - ny

y + ny

x + nxx - nx x, y

DistributeSand() – Check Neighbor Heights Example: x = 0, y = 0, r = 3 (ny = 3) y – ny <= j <= y + ny => -3 <= j <= 3

j = -3nx = = = 0

x – nx <= i <= x + nx => 0 <= i <= 0 j = -2

nx = = = = 2x – nx <= i <= x + nx => -2 <= i <= 2

0, -3

0, 3

3, 0-3, 0 0, 0

DistributeSand() – Check Neighbor Heights// ignore any point (i, j) that is at the center of the circle or beyond the // edge of the terrain or beyond the edge of the circleif((j == y && i == x) ||j < 0 || j > heightmap.Length – 1 || i < 0 || i > heightmap.Width - 1) ||(j - y) * (j - y) + (i - x) * (i - x) > r * r)

continue;

DistributeSand() – Check Neighbor Heights// assuming a cone centered at (x, y) should exist, determine the cone // height reduction for the neighbor at (i, j) based on the distance // between it and the source pole at (x, y)distance = sqrt( (i-x)*(i-x) + (j-y)*(j-y) );

// SLOPE = rise / run => rise = SLOPE * runchr = SLOPE * distance;expectedHeight = height – chr;

x, y i, jdistance

expectedHeightheight

chr

distance i, j

x, yi-x

j-y

DistributeSand() – Check Neighbor Heights// the offset of this neighbor is the amount its current height is less // than its expected heightcurHeight = heightmap [j][i] * VERTICAL_CONVERSION;offset = expectedHeight – curHeight;

x, y i, jexpectedHeight

heightoffset

curHeight

DistributeSand() – Check Neighbor Heights// if this offset is greater than the highest known offset, then update // the selection of the best neighbor if (offset > bestNeighbor.offset){

bestNeighbor.x = i;bestNeighbor.y = j;bestNeighbor.offset = offset;

}}}

Example

x = 0, y = 0, height = 18

A

B

C

1

1

68

Example – Pole A

SLOPE = 11.547

distance = sqrt( (i-x)*(i-x) + (j-y)*(j-y) );distance = = = 2chr = SLOPE * distance = 11.547 * 2 = 23.094expectedHeight = height – chr = 18 – 23.094 = -5.094curHeight = heightmap [j][i] * VERTICAL_CONVERSION = 6 * 1 = 6offset = expectedHeight – curHeight = -5.094 – 6 = -11.094Not > 0 -> don’t consider this pole

AB

C

1

1

68

Example – Pole B

distance = = = 1.414chr = SLOPE * distance = 11.547 * 1.4 = 16.166expectedHeight = height – chr = 18 – 16.166 = 1.834curHeight = heightmap [j][i] = 1offset = expectedHeight – curHeight = 1.834 – 1 = 0.834

Biggest offset so far -> record pole

AB

C

1

1

68

Example – Pole C

distance = = = 1chr = SLOPE * distance = 11.547 * 1 = 11.547expectedHeight = height – chr = 18 – 11.547 = 6.453curHeight = heightmap [j][i] = 1offset = expectedHeight – curHeight = 6.453 – 1 = 5.453

Biggest offset so far -> replace Pole B

AB

C

1

1

68

DistributeSand() – Move Sand?

// positive offset?if (bestNeighbor.offset > 0){

// move sand}else{

// keep sand at current pole// remember that height is the pole’s new heightheightmap [y][x] = height / VERTICAL_CONVERSION;

}

CreatePile() Algorithm – Move Sand

deltaHeight = height - h[y][x];// determine additional height to add to best neighbor// only give a neighbor enough to bring it up to its expected heightif (bestNeighbor.offset < deltaHeight)

extraHeight = bestNeighbor.offset;else

extraHeight = deltaHeight;

x, y i, j

expectedHeight

height offset

curHeight

CreatePile() Algorithm – Move Sand

deltaHeight -> amount we have to allocate bestNeighbor.offset -> max amount we want to allocate to neighbor extraHeight -> height that we will add to neighbour

if (bestNeighbor.offset < deltaHeight) -> if amount we want to allocate is less than the amount we have

extraHeight = bestNeighbor.offset; -> we will add an amount equal to offset, filling the hole

Else -> amount we want to allocate is more than amount we have extraHeight = deltaHeight; -> we will add as much as we have

DistributeSand() – Move Sand

// increase height of pile at best neighborDistributeSand(

bestNeighbor.x, bestNeighbor.y, heightmap[bestNeighbor.y][bestNeighbor.x] + extraHeight

);// any remaining height goes to (x, y) in height mapheightmap.Data[y, x] = (height – extraHeight) / VERTICAL_CONVERSION;

Example – Best Neighbor = Pole C

bestNeighbor.offset = 5.453 > 0 -> move sand

deltaHeight = height - h[y][x] = 18 – 8 = 10if (bestNeighbor.offset < deltaHeight) => 5.453 < 10

extraHeight = bestNeighbor.offset = 5.453

DistributeSand(bestNeighbor.x, bestNeighbor.y,heightmap[bestNeighbor.y][bestNeighbor.x] + extraHeight = 1 + 5.453 = 6.453);

heightmap.Data[y, x] = (height – extraHeight) / VERTICAL_CONVERSION; = (18 – 6.453) / 1 = 11.547

AB

C

1

1

68