RollShape: ThreeDBasics.ShapeProc ~ {
PROC[context: REF Context, shape: REF ShapeInstance, data: REF ANY ← NIL] RETURNS[REF ShapeInstance]
Rotates and finds minimum height, returns as new base to rotate on
GetBase:
PROC [shape:
REF ShapeInstance]
RETURNS[base: Triple] ~ {
base.z ← Real.LargestNumber;
FOR i:
NAT
IN [0..shape.vertex.length)
DO
newVtx: Triple ← G3dMatrix.Transform[
[shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z], shape.position
];
IF newVtx.z < base.z THEN base ← newVtx;
ENDLOOP;
};
shadowShape: REF ShapeInstance;
frameNumber: NAT ← context.frameNumber;
log: IO.STREAM ← NARROW[Atom.GetPropFromList[context.props, $Log]];
list: LORA ← NARROW[GetProp[shape.shadingProps, $RollShape] ];
rotationRate: REAL ← NARROW[ list.first, REF REAL ]^;
timePerFrame: REAL ← NARROW[ list.rest.first, REF REAL ]^;
endTime: REAL ← NARROW[ list.rest.rest.first, REF REAL ]^;
ctrOfMass:
REF Triple ←
IF list.rest.rest.rest #
NIL
THEN NARROW[ list.rest.rest.rest.first ] ELSE NIL;
xfmdCtr, lastBase: Triple;
armLength, massLength, dRot, newMassLength: REAL;
IF ctrOfMass =
NIL
THEN {
compute center of mass based on vertex locations (crude approximation)
ctrOfMass ← NEW[ Triple ← [0.0,0.0,0.0] ];
FOR i:
NAT
IN [0..shape.vertex.length)
DO
weight: REAL ← 4.0 * RealFns.Power[0.5 * (1.0 - shape.vertex[i].z), 3.0]
* RealFns.SqRt[Sqr[shape.vertex[i].x] + Sqr[shape.vertex[i].y]];
ctrOfMass.x ← ctrOfMass.x + shape.vertex[i].x*weight;
ctrOfMass.y ← ctrOfMass.y + shape.vertex[i].y*weight;
ctrOfMass.z ← ctrOfMass.z + shape.vertex[i].z*weight;
ENDLOOP;
ctrOfMass.x ← ctrOfMass.x / shape.vertex.length;
ctrOfMass.y ← ctrOfMass.y / shape.vertex.length;
ctrOfMass.z ← ctrOfMass.z / shape.vertex.length;
};
xfmdCtr ← G3dMatrix.Transform[ ctrOfMass^, shape.position ]; -- Evaluate new center of mass
armLength ← G3dVector.Length[
G3dVector.Sub[ xfmdCtr, [shape.axisBase.x, shape.axisBase.y, xfmdCtr.z] ]
];
massLength ← G3dVector.Length[ G3dVector.Sub[xfmdCtr, shape.axisBase] ];
Get acceleration due to center of mass and base position
dRot ← (force / massLength) * armLength / massLength;
IF (((shape.axisEnd.x - shape.axisBase.x) * (xfmdCtr.y - shape.axisBase.y))
-- which way?
- ((shape.axisEnd.y - shape.axisBase.y) * (xfmdCtr.x - shape.axisBase.x))) < 0
THEN dRot ← -dRot;
rotationRate ← rotationRate + dRot * radiansToDegrees; -- Adjust rotation rate
IF log #
NIL
THEN log.PutRope[ Rope.Cat[
" Rate - ", Convert.RopeFromReal[rotationRate],
", delta - ", Convert.RopeFromReal[dRot], " "
] ];
shape.rotation ← rotationRate * timePerFrame; -- Rotate shape incrementally
lastBase ← shape.axisBase;
lastBase.z ← shape.axisBase.z + shape.location.z;
shape.axisBase ← GetBase[shape]; -- get new base for axis
shape.axisEnd ← G3dVector.Add[ shape.axisEnd, G3dVector.Sub[shape.axisBase, lastBase] ];
shape.location.z ← lastBase.z - shape.axisBase.z; -- keep above surface
ThreeDBasics.SetPosition[shape: shape, concat: TRUE]; -- concatenate new rotation
shape.vtcesInValid ← TRUE;
Get acceleration due to change in length of moment arm
xfmdCtr ← G3dMatrix.Transform[ ctrOfMass^, shape.position ]; -- Evaluate new center of mass
newMassLength ← G3dVector.Length[ G3dVector.Sub[xfmdCtr, shape.axisBase] ];
rotationRate ← rotationRate * Sqr[massLength / newMassLength];
Damp rotation rate just to get things to stop eventually
rotationRate ← rotationRate
* RealFns.Power[(endTime - frameNumber * timePerFrame) / endTime, 1.0];
shape.shadingProps ← PutProp[
-- Update parameters on property list
shape.shadingProps, $RollShape,
LIST[ NEW[REAL ← rotationRate], NEW[REAL ← timePerFrame], NEW[REAL ← endTime],
ctrOfMass ]
];
shadowShape ← SceneUtilities.FindShape[context, "EggShadow"];
shadowShape.location ← G3dVector.Add[ shape.axisBase, [0.0, 0.1, -0.1] ];
ThreeDBasics.SetPosition[shape: shadowShape]; -- set new location under rolling egg
shadowShape.vtcesInValid ← TRUE;
RETURN[shape];
};