Alien.cpp
// |
// Author: Philip Rideout |
// Copyright: 2002-2006 3Dlabs Inc. Ltd. All rights reserved. |
// License: see 3Dlabs-license.txt |
// |
#define _USE_MATH_DEFINES |
#include <math.h> |
#include <stdio.h> |
#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; |
} |
Copyright © 2006 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2006-07-17