views:

19

answers:

2

I've got a little objective-c utility program that renders a convex hull. (This is to troubleshoot a bug in another program that calculates the convex hull in preparation for spatial statistical analysis). I'm trying to render a set of triangles, each with an outward-pointing vector. I can get the triangles without problems, but the vectors are driving me crazy.

I'd like the vectors to be simple cylinders. The problem is that I can't just declare coordinates for where the top and bottom of the cylinders belong in 3D (e.g., like I can for the triangles). I have to make them and then rotate and translate them from their default position along the z-axis. I've read a ton about Euler angles, and angle-axis rotations, and quaternions, most of which is relevant, but not directed at what I need: most people have a set of objects and then need to rotate the object in response to some input. I need to place the object correctly in the 3D "scene".

I'm using the Cocoa3DTutorial classes to help me out, and they work great as far as I can tell, but the rotation bit is killing me.

Here is my current effort. It gives me cylinders that are located correctly, but all point along the z-axis (as in this image:alt text. We are looking in the -z direction. The triangle poking out behind is not part of the hull; for testing/debugging. The orthogonal cylinders are coordinate axes, more or less, and the spheres are to make sure the axes are located correctly, since I have to use rotation to place those cylinders correctly. And BTW, when I use that algorithm, the out-vectors fail as well, although in a different way, coming out normal to the planes, but all pointing in +z instead of some in -z)

from Render3DDocument.m:

// Make the out-pointing vector
C3DTCylinder  *outVectTube;
C3DTEntity    *outVectEntity;
Point3DFloat  *sideCtr = [thisSide centerOfMass];
outVectTube = [C3DTCylinder cylinderWithBase: tubeRadius top: tubeRadius height: tubeRadius*10 slices: 16 stacks: 16];
outVectEntity = [C3DTEntity entityWithStyle:triColor
                   geometry:outVectTube];
Point3DFloat *outVect = [[thisSide inVect] opposite];
Point3DFloat *unitZ = [Point3DFloat pointWithX:0 Y:0 Z:1.0f];
Point3DFloat *rotAxis = [outVect crossWith:unitZ];
double rotAngle = [outVect angleWith:unitZ];
[outVectEntity setRotationX: rotAxis.x
              Y: rotAxis.y
              Z: rotAxis.z
              W: rotAngle];
[outVectEntity setTranslationX:sideCtr.x - ctrX  
              Y:sideCtr.y - ctrY
              Z:sideCtr.z - ctrZ];
[aScene addChild:outVectEntity];

(Note that Point3DFloat is basically a vector class, and that a Side (like thisSide) is a set of four Point3DFloats, one for each vertex, and one for a vector that points towards the center of the hull).

from C3DTEntity.m:

if (_hasTransform) {
    glPushMatrix();

    // Translation
    if ((_translation.x != 0.0) || (_translation.y != 0.0) || (_translation.z != 0.0)) {
        glTranslatef(_translation.x, _translation.y, _translation.z);
    }

    // Scaling
    if ((_scaling.x != 1.0) || (_scaling.y != 1.0) || (_scaling.z != 1.0)) {
        glScalef(_scaling.x, _scaling.y, _scaling.z);
    }

    // Rotation
    glTranslatef(-_rotationCenter.x, -_rotationCenter.y, -_rotationCenter.z);

    if (_rotation.w != 0.0) {
        glRotatef(_rotation.w, _rotation.x, _rotation.y, _rotation.z);
    } else {
        if (_rotation.x != 0.0)
            glRotatef(_rotation.x, 1.0f, 0.0f, 0.0f);
        if (_rotation.y != 0.0)
            glRotatef(_rotation.y, 0.0f, 1.0f, 0.0f);
        if (_rotation.z != 0.0)
            glRotatef(_rotation.z, 0.0f, 0.0f, 1.0f);
    }

    glTranslatef(_rotationCenter.x, _rotationCenter.y, _rotationCenter.z);
}

I added the bit in the above code that uses a single rotation around an axis (the "if (_rotation.w != 0.0)" bit), rather than a set of three rotations. My code is likely the problem, but I can't see how.

A: 

If your outvects don't all point in the correct directino, you might have to check your triangles' winding - are they all oriented the same way?

Additionally, it might be helpful to draw a line for each outvec (Use the average of the three vertices of your triangle as origin, and draw a line of a few units' length (depending on your scene's scale) into the direction of the outvect. This way, you can be sure that all your vectors are oriented correctly.

How do you calculate your outvects?

sum1stolemyname
The winding is irrelevant here. My other program keeps track of outvects (well, actually inVects as you can see from the code) and saves them along with the Side data to a text file that this program is reading as input. I need to be able to render not the correct outVect, but the outVect that my other program *thinks* is correct. This program has no responsibility to create or track true outVects, only to render cylinders correctly to represent them.
Dave Hirsch
A: 

The problem appears to be in that glrotatef() expects degrees and I was giving it radians. In addition, clockwise rotation is taken to be positive, and so the sign of the rotation was wrong. This is the corrected code:

double rotAngle = -[outVect angleWith:unitZ];   // radians
[outVectEntity setRotationX: rotAxis.x
                          Y: rotAxis.y
                          Z: rotAxis.z
                          W: rotAngle * 180.0 / M_PI ];

I can now see that my other program has the inVects wrong (the outVects below are poking through the hull instead of pointing out from each face), and I can now track down that bug in the other program...tomorrow: alt text

Dave Hirsch