Animating a Growing Pile of Sand By Karlee Stein Based on research by Hui Gong and Howard Hamilton.
-
Upload
rafe-carson -
Category
Documents
-
view
220 -
download
3
Transcript of Animating a Growing Pile of Sand By 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 – 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