# Alien.cpp

 // // Author: Philip Rideout // Copyright: 2002-2006 3Dlabs Inc. Ltd. All rights reserved. // License: see 3Dlabs-license.txt // #define _USE_MATH_DEFINES #include #include #include "Alien.h" #include "os.h" // The alien's coordinates are specified in [-300,+300] for convenience. const float Extent = 300; // The ellipsoids for the eyes are elongated along the y-axis. const vec2 TEllipsoid::radii(10, 20); // Define the points along the surface of revolution. const float data[8][2] = { {0, 240}, // 0 {100, 240}, // 1 {70, 140}, // 2 {5, 140}, // 3 {5, 90}, // 4 {100, 40}, // 5 {125, -130}, // 6 {0, -110}, // 7 }; #define slices 20 #define BLANK {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} // x, y, jointWidth, radius, length0, length1, angle0, angle1, transform0, transform1 TJoint joints[TAlien::NumJoints] = { {37.5, 227.5, .5, 4, 60, 40, 40, -20, BLANK, BLANK}, // right antenna {-37.5,227.5, .5, 4, 60, 40, 135, 20, BLANK, BLANK}, // left antenna {60, 30, .5, 6, 100, 50, 45, -20, BLANK, BLANK}, // right arm {-60, 30, .5, 6, 100, 50, 135, 20, BLANK, BLANK}, // left arm {50, -100, .5, 10, 50, 150, -80, -10, BLANK, BLANK}, // right leg {-50, -100, .5, 10, 50, 150, -100, 10, BLANK, BLANK}, // left leg }; TAlien::TAlien() { for (int i = 0; i < 5; ++i) { animation.push_back(TAlienMotion(1.5)); animation.back().add(RightArmBase, -5); animation.back().add(RightArmExt, -25); animation.push_back(TAlienMotion(1.5)); animation.back().add(RightArmBase, 5); animation.back().add(RightArmExt, 25); } animation.push_back(TAlienMotion(20)); animation.back().add(RightArmBase, -5); animation.back().add(LeftArmBase, 5); animation.back().add(LeftArmExt, -10); animation.back().add(RightArmExt, 5); animation.push_back(TAlienMotion(20)); animation.back().add(LeftAntennaExt, -5); animation.back().add(RightAntennaBase, 5); animation.back().add(LeftLegBase, -5); animation.back().add(LeftLegExt, 5); for (int i = 0; i < 5; ++i) { animation.push_back(TAlienMotion(1.5)); animation.back().add(RightArmBase, -5); animation.back().add(RightArmExt, -25); animation.push_back(TAlienMotion(1.5)); animation.back().add(RightArmBase, 5); animation.back().add(RightArmExt, 25); } animation.push_back(TAlienMotion(20)); animation.back().add(LeftAntennaExt, 5); animation.back().add(RightAntennaBase, -5); animation.back().add(LeftLegBase, 5); animation.back().add(LeftLegExt, -5); animation.push_back(TAlienMotion(20)); animation.back().add(RightArmBase, 5); animation.back().add(LeftArmBase, -5); animation.back().add(LeftArmExt, 10); animation.back().add(RightArmExt, -5); memset(surface, 0, sizeof(surface)); } TAlien::~TAlien() { for (int i = 0; i < NumSurfaces; ++i) delete surface[i]; } void TAlien::Load(GLhandleARB program_object) { motion = animation.begin(); countdown = motion->seconds; weightLocation = glGetAttribLocationARB(program_object, "weight"); // Set the default weight to 1. glVertexAttrib1fARB(weightLocation, 1.0f); // Delete old surfaces (needed after refreshing) for (int i = 0; i < TAlien::NumSurfaces; ++i) delete surface[i]; // Create the surfaces. for (int i = 0; i < TAlien::NumJoints; ++i) surface[i] = new TAppendage(joints[i], weightLocation); surface[TAlien::NumJoints + 0] = new TRevolveBezier(0, 1, 2, 3); // head surface[TAlien::NumJoints + 1] = new TRevolveLine(3, 4); // neck surface[TAlien::NumJoints + 2] = new TRevolveBezier(4, 5, 6, 7); // body surface[TAlien::NumJoints + 3] = new TEllipsoid(25, 190, 50); // right eye surface[TAlien::NumJoints + 4] = new TEllipsoid(-25, 190, 50); // left eye // Set up some default values for the uniforms and send them to the card. index0 = 0; glUniform1iARB(glGetUniformLocationARB(program_object, "index0"), index0); index1 = 0; glUniform1iARB(glGetUniformLocationARB(program_object, "index1"), index1); // Tweak the starting positions to make it interesting. Flex(3, 60); Flex(7, 60); Flex(11, 10); // Generate the transformation matrices for each joint. Update(); } void TAlien::Draw(GLhandleARB program_object) const { char name[] = "transforms[xx]"; for (int i = 0; i < NumAngles + 1; ++i) { sprintf(name, "transforms[%d]", i); glUniformMatrix4fvARB(glGetUniformLocationARB(program_object, name), 1, 0, (float*)transforms[i].data); } draw(program_object); } int TAlien::draw(GLhandleARB program_object) const { int verts = 0; // Draw each appendage using the appropriate transformation matrices. for (int i = 0; i < 6; ++i) { index0 = (i * 2 + 1); glUniform1iARB(glGetUniformLocationARB(program_object, "index0"), index0); index1 = (i * 2 + 2); glUniform1iARB(glGetUniformLocationARB(program_object, "index1"), index1); verts += surface[i]->Draw(slices); } // Turn skinning off. glVertexAttrib1fARB(weightLocation, 1.0f); index0 = 0; glUniform1iARB(glGetUniformLocationARB(program_object, "index0"), index0); // Draw the head, neck, and body. verts += surface[6]->Draw(slices); verts += surface[7]->Draw(slices); verts += surface[8]->Draw(slices); // Turn off texturing, then draw the eyes. glDisable(GL_TEXTURE_2D); static float white[] = {1, 1, 1}; glUniform3fvARB(glGetUniformLocationARB(program_object, "SkinColor"), 1, white); for (int i = NumSurfaces - 2; i < NumSurfaces; ++i) verts += surface[i]->Draw(slices); return verts; } TJoint& TAlien::GetJoint(int index) const { return ((TAppendage*) surface[index])->joint; } TRevolveBezier::TRevolveBezier(int a, int b, int c, int d) { p0.x = data[a][0]; p0.y = data[a][1]; p1.x = data[b][0]; p1.y = data[b][1]; p2.x = data[c][0]; p2.y = data[c][1]; p3.x = data[d][0]; p3.y = data[d][1]; } TRevolveLine::TRevolveLine(int a, int b) { p0 = vec2(data[a][0], data[a][1]); p1 = vec2(data[b][0], data[b][1]); } void TRevolveBezier::Eval(vec2& domain, vec3& range) { float u = 1 - domain.u; float v = domain.v * twopi; // Tweak the texture coordinates. domain.u /= 6; // Use cubic Bernstein polynomials for the Bezier basis functions. float b0 = (1 - u) * (1 - u) * (1 - u); float b1 = 3 * u * (1 - u) * (1 - u); float b2 = 3 * u * u * (1 - u); float b3 = u * u * u; vec2 p = p0 * b0 + p1 * b1 + p2 * b2 + p3 * b3; range.x = p.x * cosf(v); range.z = p.x * sinf(v); range.y = p.y; range /= Extent; } void TRevolveLine::Eval(vec2& domain, vec3& range) { float u = 1 - domain.u; float v = domain.v * twopi; float radius = p0.x + u * (p1.x - p0.x); range.x = radius * cosf(v); range.z = radius * sinf(v); range.y = p0.y + u * (p1.y - p0.y); range /= Extent; } void TEllipsoid::Eval(vec2& domain, vec3& range) { float u = fabsf(domain.u * pi); float v = fabsf(domain.v * twopi); range.x = center.x + radii.x * cosf(v) * sinf(u); range.y = center.y + radii.y * sinf(v) * sinf(u); range.z = center.z + radii.x * cosf(u); range /= Extent; } void TAppendage::Eval(vec2& domain, vec3& range) { float v = fabsf((1 - domain.v) * twopi); if (domain.u < 0.5) { float u = fabsf(domain.u * 2); range.x = u * joint.length0; range.y = joint.radius * cosf(v); range.z = joint.radius * sinf(v); } else { float u = fabsf((domain.u - 0.5F) * 2); range.x = joint.length0 + u * joint.length1; range.y = joint.radius * (1 - u) * cosf(v); range.z = joint.radius * (1 - u) * sinf(v); } range /= Extent; } // // Given a parametric value in [0,1], returns a blending weight in [0,1]. // float TAppendage::CustomAttributeValue(const vec2& domain) { float u = domain.u; if (u < 0.5f - joint.width / 2) return 1; else if (u > 0.5f + joint.width / 2) return 0; u -= 0.5f - joint.width / 2; u /= joint.width; u = 1 - u; return u; } void TAlien::Flex(int index, float delta) const { SetAngle(index, GetAngle(index) + delta); } float& TAlien::GetAngle(int index) const { return (index % 2) ? GetJoint(index/2).angle1 : GetJoint(index/2).angle0; } void TAlien::SetAngle(int index, float theta) const { if (index < 0 || index >= NumAngles) return; float& angle = GetAngle(index); angle = theta; } void TAlien::Reset() { for (int index = 0; index < NumAngles; ++index) { float originalAngle = (index % 2) ? GetJoint(index/2).angle1 : GetJoint(index/2).angle0; SetAngle(index, originalAngle); } } mat4& TAlien::GetTransform(int index) { return (index % 2) ? GetJoint(index/2).transform1 : GetJoint(index/2).transform0; } // Flip the normals in the center of his eyes to create black pupils. bool TEllipsoid::Flip(const vec2& domain) { return (domain.u < 0.125f); } void TAlien::Update() { animate(); glPushMatrix(); // Squish him in Z so he isn't so round and fat. glScalef(1, 1, 0.5F); // Calculate the modelview-projection matrix. mat4 projection; mat4 modelview; glGetFloatv(GL_PROJECTION_MATRIX, projection.data); glGetFloatv(GL_MODELVIEW_MATRIX, modelview.data); mat4 mvp = projection * modelview; transforms[0] = mvp; for (int jointIndex = 0; jointIndex < NumJoints; ++jointIndex) { TJoint& joint = GetJoint(jointIndex); glPushMatrix(); glTranslatef(joint.x / Extent, joint.y / Extent, 0); glRotatef(joint.angle0, 0, 0, 1); glGetFloatv(GL_MODELVIEW_MATRIX, joint.transform0.data); glTranslatef(joint.length0 / Extent, 0, 0); glRotatef(joint.angle1, 0, 0, 1); glTranslatef(-joint.length0 / Extent, 0, 0); glGetFloatv(GL_MODELVIEW_MATRIX, joint.transform1.data); glPopMatrix(); transforms[1 + jointIndex * 2] = projection * joint.transform0; transforms[2 + jointIndex * 2] = projection * joint.transform1; } glPopMatrix(); } void TAlien::animate() { if (animation.empty()) return; const float interval = 100; countdown -= interval; if (countdown <= 0) { if (++motion == animation.end()) { motion = animation.begin(); Reset(); } countdown = 1000 * motion->seconds; } for (TAlienMovements::const_iterator i = motion->movements.begin(); i != motion->movements.end(); ++i) Flex(i->joint, i->dps * interval / 1000.0f); } void* newAlien(GLhandleARB program_object, int time) { TAlien* alien = new TAlien(); alien->Load(program_object); return alien; } void updateDrawAlien(void* newAlien, GLhandleARB program_object, int time) { TAlien* alien = (TAlien*)newAlien; alien->Update(); alien->Draw(program_object); } void deleteAlien(void* newAlien) { TAlien* alien = (TAlien*)newAlien; delete alien; }