999 lines
28 KiB
C++
999 lines
28 KiB
C++
/* --------------------------------------------------------------------
|
|
EXTREME TUXRACER
|
|
|
|
Copyright (C) 1999-2001 Jasmin F. Patry (Tuxracer)
|
|
Copyright (C) 2010 Extreme Tuxracer Team
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
|
|
This module has been completely rewritten. Remember that the way of
|
|
defining the character has radically changed though the character is
|
|
still shaped with spheres.
|
|
---------------------------------------------------------------------*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <etr_config.h>
|
|
#endif
|
|
|
|
#include "tux.h"
|
|
#include "ogl.h"
|
|
#include "spx.h"
|
|
#include "textures.h"
|
|
#include "course.h"
|
|
#include "physics.h"
|
|
|
|
#define MAX_ARM_ANGLE2 30.0
|
|
#define MAX_PADDLING_ANGLE2 35.0
|
|
#define MAX_EXT_PADDLING_ANGLE2 30.0
|
|
#define MAX_KICK_PADDLING_ANGLE2 20.0
|
|
#define TO_AIR_TIME 0.5
|
|
#define TO_TIME 0.14
|
|
|
|
#define SHADOW_HEIGHT 0.03 // ->0.05
|
|
|
|
#ifdef USE_STENCIL_BUFFER
|
|
static const TColor shad_col(0.0, 0.0, 0.0, 0.3);
|
|
#else
|
|
static const TColor shad_col(0.0, 0.0, 0.0, 0.1);
|
|
#endif
|
|
|
|
static const TCharMaterial TuxDefMat = {TColor(0.5, 0.5, 0.5, 1.0), TColor(0.0, 0.0, 0.0, 1.0), 0.0};
|
|
static const TCharMaterial Highlight = {TColor(0.8, 0.15, 0.15, 1.0), TColor(0.0, 0.0, 0.0, 1.0), 0.0};
|
|
CCharShape TestChar;
|
|
|
|
CCharShape::CCharShape () {
|
|
for (int i=0; i<MAX_CHAR_NODES; i++) {
|
|
Nodes[i] = NULL;
|
|
Index[i] = -1;
|
|
}
|
|
numNodes = 0;
|
|
|
|
useActions = false;
|
|
newActions = false;
|
|
useMaterials = true;
|
|
useHighlighting = false;
|
|
highlighted = false;
|
|
highlight_node = -1;
|
|
}
|
|
|
|
CCharShape::~CCharShape() {
|
|
for (int i=0; i<MAX_CHAR_NODES; i++) {
|
|
if (Nodes[i] != NULL) {
|
|
delete Nodes[i]->action;
|
|
delete Nodes[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// nodes
|
|
// --------------------------------------------------------------------
|
|
|
|
size_t CCharShape::GetNodeIdx (size_t node_name) const {
|
|
if (node_name >= MAX_CHAR_NODES) return -1;
|
|
return Index[node_name];
|
|
}
|
|
|
|
TCharNode *CCharShape::GetNode (size_t node_name) {
|
|
size_t idx = GetNodeIdx (node_name);
|
|
if (idx >= numNodes) return NULL;
|
|
return Nodes[idx];
|
|
}
|
|
|
|
void CCharShape::CreateRootNode () {
|
|
TCharNode *node = new TCharNode;
|
|
node->node_name = 0;
|
|
node->parent = NULL;
|
|
node->parent_name = 99;
|
|
node->next = NULL;
|
|
node->next_name = 99;
|
|
node->child = NULL;
|
|
node->child_name = 99;
|
|
node->mat = NULL;
|
|
node->joint = "root";
|
|
node->render_shadow = false;
|
|
node->visible = false;
|
|
node->action = NULL;
|
|
MakeIdentityMatrix (node->trans);
|
|
MakeIdentityMatrix (node->invtrans);
|
|
|
|
NodeIndex.clear();
|
|
NodeIndex["root"] = 0;
|
|
Index[0] = 0;
|
|
Nodes[0] = node;
|
|
numNodes = 1;
|
|
}
|
|
|
|
bool CCharShape::CreateCharNode
|
|
(int parent_name, size_t node_name, const string& joint,
|
|
const string& name, const string& order, bool shadow) {
|
|
|
|
TCharNode *parent = GetNode (parent_name);
|
|
if (parent == NULL) {
|
|
Message ("wrong parent node");
|
|
return false;
|
|
}
|
|
TCharNode *node = new TCharNode;
|
|
node->node_name = node_name;
|
|
node->parent = parent;
|
|
node->parent_name = parent_name;
|
|
node->next = NULL;
|
|
node->next_name = 99;
|
|
node->child = NULL;
|
|
node->child_name = 99;
|
|
|
|
if (useActions) {
|
|
node->action = new TCharAction;
|
|
node->action->num = 0;
|
|
node->action->name = name;
|
|
node->action->order = order;
|
|
node->action->mat = "";
|
|
} else
|
|
node->action = NULL;
|
|
|
|
node->mat = NULL;
|
|
node->node_idx = numNodes;
|
|
node->visible = false;
|
|
node->render_shadow = shadow;
|
|
node->joint = joint;
|
|
|
|
MakeIdentityMatrix (node->trans);
|
|
MakeIdentityMatrix (node->invtrans);
|
|
|
|
if (!joint.empty()) NodeIndex[joint] = node_name;
|
|
Nodes[numNodes] = node;
|
|
Index[node_name] = numNodes;
|
|
|
|
/// -------------------------------------------------------------------
|
|
if (parent->child == NULL) {
|
|
parent->child = node;
|
|
parent->child_name = node_name;
|
|
} else {
|
|
for (parent = parent->child; parent->next != NULL; parent = parent->next) {}
|
|
parent->next = node;
|
|
parent->next_name = node_name;
|
|
}
|
|
/// -------------------------------------------------------------------
|
|
|
|
numNodes++;
|
|
return true;
|
|
}
|
|
|
|
void CCharShape::AddAction (size_t node_name, int type, const TVector3& vec, double val) {
|
|
size_t idx = GetNodeIdx (node_name);
|
|
TCharAction *act = Nodes[idx]->action;
|
|
act->type[act->num] = type;
|
|
act->vec[act->num] = vec;
|
|
act->dval[act->num] = val;
|
|
act->num++;
|
|
}
|
|
|
|
bool CCharShape::TranslateNode (size_t node_name, const TVector3& vec) {
|
|
TCharNode *node = GetNode (node_name);
|
|
if (node == NULL) return false;
|
|
|
|
TMatrix TransMatrix;
|
|
|
|
MakeTranslationMatrix (TransMatrix, vec.x, vec.y, vec.z);
|
|
MultiplyMatrices (node->trans, node->trans, TransMatrix);
|
|
MakeTranslationMatrix (TransMatrix, -vec.x, -vec.y, -vec.z);
|
|
MultiplyMatrices (node->invtrans, TransMatrix, node->invtrans);
|
|
|
|
if (newActions && useActions) AddAction (node_name, 0, vec, 0);
|
|
return true;
|
|
}
|
|
|
|
bool CCharShape::RotateNode (size_t node_name, int axis, double angle) {
|
|
TCharNode *node = GetNode (node_name);
|
|
if (node == NULL) return false;
|
|
|
|
if (axis > 3) return false;
|
|
|
|
TMatrix rotMatrix;
|
|
char caxis = '0';
|
|
switch (axis) {
|
|
case 1:
|
|
caxis = 'x';
|
|
break;
|
|
case 2:
|
|
caxis = 'y';
|
|
break;
|
|
case 3:
|
|
caxis = 'z';
|
|
break;
|
|
}
|
|
|
|
MakeRotationMatrix (rotMatrix, angle, caxis);
|
|
MultiplyMatrices (node->trans, node->trans, rotMatrix);
|
|
MakeRotationMatrix (rotMatrix, -angle, caxis);
|
|
MultiplyMatrices (node->invtrans, rotMatrix, node->invtrans);
|
|
|
|
if (newActions && useActions) AddAction (node_name, axis, NullVec, angle);
|
|
return true;
|
|
}
|
|
|
|
bool CCharShape::RotateNode (const string& node_trivialname, int axis, double angle) {
|
|
map<string, size_t>::const_iterator i = NodeIndex.find(node_trivialname);
|
|
if (i == NodeIndex.end()) return false;
|
|
return RotateNode (i->second, axis, angle);
|
|
}
|
|
|
|
void CCharShape::ScaleNode (size_t node_name, const TVector3& vec) {
|
|
TCharNode *node = GetNode(node_name);
|
|
if (node == NULL) return;
|
|
|
|
TMatrix matrix;
|
|
|
|
MakeIdentityMatrix (matrix);
|
|
MultiplyMatrices (node->trans, node->trans, matrix);
|
|
MakeIdentityMatrix (matrix);
|
|
MultiplyMatrices (node->invtrans, matrix, node->invtrans);
|
|
|
|
MakeScalingMatrix (matrix, vec.x, vec.y, vec.z);
|
|
MultiplyMatrices (node->trans, node->trans, matrix);
|
|
MakeScalingMatrix (matrix, 1.0 / vec.x, 1.0 / vec.y, 1.0 / vec.z);
|
|
MultiplyMatrices (node->invtrans, matrix, node->invtrans);
|
|
|
|
MakeIdentityMatrix (matrix);
|
|
MultiplyMatrices (node->trans, node->trans, matrix);
|
|
MakeIdentityMatrix (matrix);
|
|
MultiplyMatrices (node->invtrans, matrix, node->invtrans);
|
|
|
|
if (newActions && useActions) AddAction (node_name, 4, vec, 0);
|
|
}
|
|
|
|
bool CCharShape::VisibleNode (size_t node_name, float level) {
|
|
TCharNode *node = GetNode(node_name);
|
|
if (node == NULL) return false;
|
|
|
|
node->visible = (level > 0);
|
|
|
|
if (node->visible) {
|
|
node->divisions =
|
|
MIN (MAX_SPHERE_DIV, MAX (MIN_SPHERE_DIV,
|
|
ROUND_TO_NEAREST (param.tux_sphere_divisions * level / 10)));
|
|
node->radius = 1.0;
|
|
}
|
|
if (newActions && useActions) AddAction (node_name, 5, NullVec, level);
|
|
return true;
|
|
}
|
|
|
|
bool CCharShape::MaterialNode (size_t node_name, const string& mat_name) {
|
|
TCharNode *node = GetNode(node_name);
|
|
if (node == NULL) return false;
|
|
TCharMaterial *mat = GetMaterial(mat_name);
|
|
if (mat == NULL) return false;
|
|
node->mat = mat;
|
|
if (newActions && useActions) node->action->mat = mat_name;
|
|
return true;
|
|
}
|
|
|
|
bool CCharShape::ResetNode (size_t node_name) {
|
|
TCharNode *node = GetNode(node_name);
|
|
if (node == NULL) return false;
|
|
|
|
MakeIdentityMatrix (node->trans);
|
|
MakeIdentityMatrix (node->invtrans);
|
|
return true;
|
|
}
|
|
|
|
bool CCharShape::ResetNode (const string& node_trivialname) {
|
|
map<string, size_t>::const_iterator i = NodeIndex.find(node_trivialname);
|
|
if (i == NodeIndex.end()) return false;
|
|
return ResetNode (i->second);
|
|
}
|
|
|
|
bool CCharShape::TransformNode (size_t node_name, const TMatrix mat, const TMatrix invmat) {
|
|
TCharNode *node = GetNode(node_name);
|
|
if (node == NULL) return false;
|
|
|
|
MultiplyMatrices (node->trans, node->trans, mat);
|
|
MultiplyMatrices (node->invtrans, invmat, node->invtrans);
|
|
return true;
|
|
}
|
|
|
|
void CCharShape::ResetRoot () {
|
|
ResetNode (0);
|
|
}
|
|
|
|
void CCharShape::ResetJoints () {
|
|
ResetNode ("left_shldr");
|
|
ResetNode ("right_shldr");
|
|
ResetNode ("left_hip");
|
|
ResetNode ("right_hip");
|
|
ResetNode ("left_knee");
|
|
ResetNode ("right_knee");
|
|
ResetNode ("left_ankle");
|
|
ResetNode ("right_ankle");
|
|
ResetNode ("tail");
|
|
ResetNode ("neck");
|
|
ResetNode ("head");
|
|
}
|
|
|
|
void CCharShape::Reset () {
|
|
for (int i=0; i<MAX_CHAR_NODES; i++) {
|
|
if (Nodes[i] != NULL) {
|
|
delete Nodes[i]->action;
|
|
delete Nodes[i];
|
|
Nodes[i] = NULL;
|
|
}
|
|
Index[i] = -1;
|
|
}
|
|
Materials.clear();
|
|
NodeIndex.clear();
|
|
MaterialIndex.clear();
|
|
numNodes = 0;
|
|
|
|
useActions = true;
|
|
newActions = false;
|
|
useMaterials = true;
|
|
useHighlighting = false;
|
|
highlighted = false;
|
|
highlight_node = -1;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// materials
|
|
// --------------------------------------------------------------------
|
|
|
|
TCharMaterial* CCharShape::GetMaterial (const string& mat_name) {
|
|
map<string, size_t>::const_iterator i = MaterialIndex.find(mat_name);
|
|
if (i != MaterialIndex.end() && i->second < Materials.size()) {
|
|
return &Materials[i->second];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CCharShape::CreateMaterial (const string& line) {
|
|
TVector3 diff = SPVector3N (line, "diff", TVector3 (0,0,0));
|
|
TVector3 spec = SPVector3N (line, "spec", TVector3 (0,0,0));
|
|
float exp = SPFloatN (line, "exp", 50);
|
|
std::string mat = SPItemN (line, "mat");
|
|
STrimN(mat);
|
|
|
|
Materials.push_back(TCharMaterial());
|
|
Materials.back().diffuse.r = diff.x;
|
|
Materials.back().diffuse.g = diff.y;
|
|
Materials.back().diffuse.b = diff.z;
|
|
Materials.back().diffuse.a = 1.0;
|
|
Materials.back().specular.r = spec.x;
|
|
Materials.back().specular.g = spec.y;
|
|
Materials.back().specular.b = spec.z;
|
|
Materials.back().specular.a = 1.0;
|
|
Materials.back().exp = exp;
|
|
if (useActions)
|
|
Materials.back().matline = line;
|
|
|
|
MaterialIndex[mat] = Materials.size()-1;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// drawing
|
|
// --------------------------------------------------------------------
|
|
|
|
void CCharShape::DrawCharSphere (int num_divisions) {
|
|
GLUquadricObj *qobj = gluNewQuadric();
|
|
gluQuadricDrawStyle (qobj, GLU_FILL);
|
|
gluQuadricOrientation (qobj, GLU_OUTSIDE);
|
|
gluQuadricNormals (qobj, GLU_SMOOTH);
|
|
gluSphere (qobj, 1.0, (GLint)2.0 * num_divisions, num_divisions);
|
|
gluDeleteQuadric (qobj);
|
|
}
|
|
|
|
void CCharShape::DrawNodes (const TCharNode *node) {
|
|
glPushMatrix();
|
|
glMultMatrixd ((double *) node->trans);
|
|
|
|
if (node->node_name == highlight_node) highlighted = true;
|
|
const TCharMaterial *mat;
|
|
if (highlighted && useHighlighting) {
|
|
mat = &Highlight;
|
|
} else {
|
|
if (node->mat != NULL && useMaterials) mat = node->mat;
|
|
else mat = &TuxDefMat;
|
|
}
|
|
|
|
if (node->visible == true) {
|
|
set_material (mat->diffuse, mat->specular, mat->exp);
|
|
|
|
DrawCharSphere (node->divisions);
|
|
}
|
|
// -------------- recursive loop -------------------------------------
|
|
TCharNode *child = node->child;
|
|
while (child != NULL) {
|
|
DrawNodes (child);
|
|
if (child->node_name == highlight_node) highlighted = false;
|
|
child = child->next;
|
|
}
|
|
// -------------------------------------------------------------------
|
|
glPopMatrix();
|
|
}
|
|
|
|
void CCharShape::Draw () {
|
|
static const float dummy_color[] = {0.0, 0.0, 0.0, 1.0};
|
|
|
|
glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, dummy_color);
|
|
ScopedRenderMode rm(TUX);
|
|
glEnable (GL_NORMALIZE);
|
|
|
|
TCharNode *node = GetNode(0);
|
|
if (node == NULL) return;
|
|
|
|
DrawNodes (node);
|
|
glDisable (GL_NORMALIZE);
|
|
if (param.perf_level > 2 && g_game.argument == 0) DrawShadow ();
|
|
highlighted = false;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
bool CCharShape::Load (const string& dir, const string& filename, bool with_actions) {
|
|
CSPList list (500);
|
|
|
|
useActions = with_actions;
|
|
CreateRootNode ();
|
|
newActions = true;
|
|
|
|
if (!list.Load (dir, filename)) {
|
|
Message ("could not load character", filename);
|
|
return false;
|
|
}
|
|
|
|
for (size_t i=0; i<list.Count(); i++) {
|
|
const string& line = list.Line(i);
|
|
int node_name = SPIntN (line, "node", -1);
|
|
int parent_name = SPIntN (line, "par", -1);
|
|
string mat_name = SPStrN (line, "mat", "");
|
|
string name = SPStrN (line, "joint", "");
|
|
string fullname = SPStrN (line, "name", "");
|
|
|
|
if (SPIntN (line, "material", 0) > 0) {
|
|
CreateMaterial (line);
|
|
} else {
|
|
double visible = SPFloatN (line, "vis", -1.0);
|
|
bool shadow = SPBoolN (line, "shad", false);
|
|
string order = SPStrN (line, "order", "");
|
|
CreateCharNode (parent_name, node_name, name, fullname, order, shadow);
|
|
TVector3 rot = SPVector3N (line, "rot", NullVec);
|
|
MaterialNode (node_name, mat_name);
|
|
for (size_t ii = 0; ii < order.size(); ii++) {
|
|
int act = order[ii]-48;
|
|
switch (act) {
|
|
case 0: {
|
|
TVector3 trans = SPVector3N (line, "trans", TVector3 (0,0,0));
|
|
TranslateNode (node_name, trans);
|
|
break;
|
|
}
|
|
case 1:
|
|
RotateNode (node_name, 1, rot.x);
|
|
break;
|
|
case 2:
|
|
RotateNode (node_name, 2, rot.y);
|
|
break;
|
|
case 3:
|
|
RotateNode (node_name, 3, rot.z);
|
|
break;
|
|
case 4: {
|
|
TVector3 scale = SPVector3N (line, "scale", TVector3 (1,1,1));
|
|
ScaleNode (node_name, scale);
|
|
break;
|
|
}
|
|
case 5:
|
|
VisibleNode (node_name, visible);
|
|
break;
|
|
case 9:
|
|
RotateNode (node_name, 2, rot.z);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
newActions = false;
|
|
return true;
|
|
}
|
|
|
|
TVector3 CCharShape::AdjustRollvector (const CControl *ctrl, TVector3 vel, const TVector3& zvec) {
|
|
TMatrix rot_mat;
|
|
vel = ProjectToPlane (zvec, vel);
|
|
NormVector (vel);
|
|
if (ctrl->is_braking) {
|
|
RotateAboutVectorMatrix (rot_mat, vel, ctrl->turn_fact * BRAKING_ROLL_ANGLE);
|
|
} else {
|
|
RotateAboutVectorMatrix (rot_mat, vel, ctrl->turn_fact * MAX_ROLL_ANGLE);
|
|
}
|
|
return TransformVector (rot_mat, zvec);
|
|
}
|
|
|
|
void CCharShape::AdjustOrientation (CControl *ctrl, double dtime,
|
|
double dist_from_surface, const TVector3& surf_nml) {
|
|
TVector3 new_y, new_z;
|
|
TMatrix cob_mat, inv_cob_mat;
|
|
TMatrix rot_mat;
|
|
static const TVector3 minus_z_vec(0, 0, -1);
|
|
static const TVector3 y_vec(0, 1, 0);
|
|
|
|
if (dist_from_surface > 0) {
|
|
new_y = ScaleVector (1, ctrl->cvel);
|
|
NormVector (new_y);
|
|
new_z = ProjectToPlane (new_y, TVector3(0, -1, 0));
|
|
NormVector (new_z);
|
|
new_z = AdjustRollvector (ctrl, ctrl->cvel, new_z);
|
|
} else {
|
|
new_z = ScaleVector (-1, surf_nml);
|
|
new_z = AdjustRollvector (ctrl, ctrl->cvel, new_z);
|
|
new_y = ProjectToPlane (surf_nml, ScaleVector (1, ctrl->cvel));
|
|
NormVector(new_y);
|
|
}
|
|
|
|
TVector3 new_x = CrossProduct (new_y, new_z);
|
|
MakeBasismatrix_Inv (cob_mat, inv_cob_mat, new_x, new_y, new_z);
|
|
TQuaternion new_orient = MakeQuaternionFromMatrix (cob_mat);
|
|
|
|
if (!ctrl->orientation_initialized) {
|
|
ctrl->orientation_initialized = true;
|
|
ctrl->corientation = new_orient;
|
|
}
|
|
|
|
double time_constant = dist_from_surface > 0 ? TO_AIR_TIME : TO_TIME;
|
|
|
|
ctrl->corientation = InterpolateQuaternions (
|
|
ctrl->corientation, new_orient,
|
|
min (dtime / time_constant, 1.0));
|
|
|
|
ctrl->plane_nml = RotateVector (ctrl->corientation, minus_z_vec);
|
|
ctrl->cdirection = RotateVector (ctrl->corientation, y_vec);
|
|
MakeMatrixFromQuaternion (cob_mat, ctrl->corientation);
|
|
|
|
// Trick rotations
|
|
new_y = TVector3 (cob_mat[1][0], cob_mat[1][1], cob_mat[1][2]);
|
|
RotateAboutVectorMatrix (rot_mat, new_y, (ctrl->roll_factor * 360));
|
|
MultiplyMatrices (cob_mat, rot_mat, cob_mat);
|
|
new_x = TVector3 (cob_mat[0][0], cob_mat[0][1], cob_mat[0][2]);
|
|
RotateAboutVectorMatrix (rot_mat, new_x, ctrl->flip_factor * 360);
|
|
MultiplyMatrices (cob_mat, rot_mat, cob_mat);
|
|
|
|
TransposeMatrix (cob_mat, inv_cob_mat);
|
|
TransformNode (0, cob_mat, inv_cob_mat);
|
|
}
|
|
|
|
void CCharShape::AdjustJoints (double turnFact, bool isBraking,
|
|
double paddling_factor, double speed,
|
|
const TVector3& net_force, double flap_factor) {
|
|
double turning_angle[2];
|
|
double paddling_angle = 0;
|
|
double ext_paddling_angle = 0;
|
|
double kick_paddling_angle = 0;
|
|
double braking_angle = 0;
|
|
double force_angle = 0;
|
|
double turn_leg_angle = 0;
|
|
double flap_angle = 0;
|
|
|
|
if (isBraking) braking_angle = MAX_ARM_ANGLE2;
|
|
|
|
paddling_angle = MAX_PADDLING_ANGLE2 * sin(paddling_factor * M_PI);
|
|
ext_paddling_angle = MAX_EXT_PADDLING_ANGLE2 * sin(paddling_factor * M_PI);
|
|
kick_paddling_angle = MAX_KICK_PADDLING_ANGLE2 * sin(paddling_factor * M_PI * 2.0);
|
|
|
|
turning_angle[0] = MAX(-turnFact,0.0) * MAX_ARM_ANGLE2;
|
|
turning_angle[1] = MAX(turnFact,0.0) * MAX_ARM_ANGLE2;
|
|
flap_angle = MAX_ARM_ANGLE2 * (0.5 + 0.5 * sin (M_PI * flap_factor * 6 - M_PI / 2));
|
|
force_angle = max (-20.0, min (20.0, -net_force.z / 300.0));
|
|
turn_leg_angle = turnFact * 10;
|
|
|
|
ResetJoints ();
|
|
|
|
RotateNode ("left_shldr", 3,
|
|
MIN (braking_angle + paddling_angle + turning_angle[0], MAX_ARM_ANGLE2) + flap_angle);
|
|
RotateNode ("right_shldr", 3,
|
|
MIN (braking_angle + paddling_angle + turning_angle[1], MAX_ARM_ANGLE2) + flap_angle);
|
|
|
|
RotateNode ("left_shldr", 2, -ext_paddling_angle);
|
|
RotateNode ("right_shldr", 2, ext_paddling_angle);
|
|
RotateNode ("left_hip", 3, -20 + turn_leg_angle + force_angle);
|
|
RotateNode ("right_hip", 3, -20 - turn_leg_angle + force_angle);
|
|
|
|
RotateNode ("left_knee", 3,
|
|
-10 + turn_leg_angle - MIN (35, speed) + kick_paddling_angle + force_angle);
|
|
RotateNode ("right_knee", 3,
|
|
-10 - turn_leg_angle - MIN (35, speed) - kick_paddling_angle + force_angle);
|
|
|
|
RotateNode ("left_ankle", 3, -20 + MIN (50, speed));
|
|
RotateNode ("right_ankle", 3, -20 + MIN (50, speed));
|
|
RotateNode ("tail", 3, turnFact * 20);
|
|
RotateNode ("neck", 3, -50);
|
|
RotateNode ("head", 3, -30);
|
|
RotateNode ("head", 2, -turnFact * 70);
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// collision
|
|
// --------------------------------------------------------------------
|
|
|
|
bool CCharShape::CheckPolyhedronCollision (const TCharNode *node, const TMatrix modelMatrix,
|
|
const TMatrix invModelMatrix, const TPolyhedron& ph) {
|
|
|
|
TMatrix newModelMatrix, newInvModelMatrix;
|
|
bool hit = false;
|
|
|
|
MultiplyMatrices (newModelMatrix, modelMatrix, node->trans);
|
|
MultiplyMatrices (newInvModelMatrix, node->invtrans, invModelMatrix);
|
|
|
|
if (node->visible) {
|
|
TPolyhedron newph = CopyPolyhedron (ph);
|
|
TransPolyhedron (newInvModelMatrix, newph);
|
|
hit = IntersectPolyhedron (newph);
|
|
FreePolyhedron (newph);
|
|
}
|
|
|
|
if (hit == true) return hit;
|
|
const TCharNode *child = node->child;
|
|
while (child != NULL) {
|
|
hit = CheckPolyhedronCollision (child, newModelMatrix, newInvModelMatrix, ph);
|
|
if (hit == true) return hit;
|
|
child = child->next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CCharShape::CheckCollision (const TPolyhedron& ph) {
|
|
TMatrix mat, invmat;
|
|
|
|
MakeIdentityMatrix (mat);
|
|
MakeIdentityMatrix (invmat);
|
|
|
|
TCharNode *node = GetNode(0);
|
|
if (node == NULL) return false;
|
|
return CheckPolyhedronCollision (node, mat, invmat, ph);
|
|
}
|
|
|
|
bool CCharShape::Collision (const TVector3& pos, const TPolyhedron& ph) {
|
|
ResetNode (0);
|
|
TranslateNode (0, TVector3 (pos.x, pos.y, pos.z));
|
|
return CheckCollision (ph);
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// shadow
|
|
// --------------------------------------------------------------------
|
|
|
|
void CCharShape::DrawShadowVertex (double x, double y, double z, const TMatrix mat) {
|
|
TVector3 pt(x, y, z);
|
|
pt = TransformPoint (mat, pt);
|
|
double old_y = pt.y;
|
|
TVector3 nml = Course.FindCourseNormal (pt.x, pt.z);
|
|
pt.y = Course.FindYCoord (pt.x, pt.z) + SHADOW_HEIGHT;
|
|
if (pt.y > old_y) pt.y = old_y;
|
|
glNormal3f (nml.x, nml.y, nml.z);
|
|
glVertex3f (pt.x, pt.y, pt.z);
|
|
}
|
|
|
|
void CCharShape::DrawShadowSphere (const TMatrix mat) {
|
|
double theta, phi, d_theta, d_phi, eps, twopi;
|
|
double x, y, z;
|
|
int div = param.tux_shadow_sphere_divisions;
|
|
|
|
eps = 1e-15;
|
|
twopi = M_PI * 2.0;
|
|
d_theta = d_phi = M_PI / div;
|
|
|
|
for (phi = 0.0; phi + eps < M_PI; phi += d_phi) {
|
|
double cos_theta, sin_theta;
|
|
double sin_phi, cos_phi;
|
|
double sin_phi_d_phi, cos_phi_d_phi;
|
|
|
|
sin_phi = sin (phi);
|
|
cos_phi = cos (phi);
|
|
sin_phi_d_phi = sin (phi + d_phi);
|
|
cos_phi_d_phi = cos (phi + d_phi);
|
|
|
|
if (phi <= eps) {
|
|
glBegin (GL_TRIANGLE_FAN);
|
|
DrawShadowVertex (0., 0., 1., mat);
|
|
|
|
for (theta = 0.0; theta + eps < twopi; theta += d_theta) {
|
|
sin_theta = sin (theta);
|
|
cos_theta = cos (theta);
|
|
|
|
x = cos_theta * sin_phi_d_phi;
|
|
y = sin_theta * sin_phi_d_phi;
|
|
z = cos_phi_d_phi;
|
|
DrawShadowVertex (x, y, z, mat);
|
|
}
|
|
x = sin_phi_d_phi;
|
|
y = 0.0;
|
|
z = cos_phi_d_phi;
|
|
DrawShadowVertex (x, y, z, mat);
|
|
glEnd();
|
|
} else if (phi + d_phi + eps >= M_PI) {
|
|
glBegin (GL_TRIANGLE_FAN);
|
|
DrawShadowVertex (0., 0., -1., mat);
|
|
for (theta = twopi; theta - eps > 0; theta -= d_theta) {
|
|
sin_theta = sin (theta);
|
|
cos_theta = cos (theta);
|
|
x = cos_theta * sin_phi;
|
|
y = sin_theta * sin_phi;
|
|
z = cos_phi;
|
|
DrawShadowVertex (x, y, z, mat);
|
|
}
|
|
x = sin_phi;
|
|
y = 0.0;
|
|
z = cos_phi;
|
|
DrawShadowVertex (x, y, z, mat);
|
|
glEnd();
|
|
} else {
|
|
glBegin (GL_TRIANGLE_STRIP);
|
|
for (theta = 0.0; theta + eps < twopi; theta += d_theta) {
|
|
sin_theta = sin (theta);
|
|
cos_theta = cos (theta);
|
|
x = cos_theta * sin_phi;
|
|
y = sin_theta * sin_phi;
|
|
z = cos_phi;
|
|
DrawShadowVertex (x, y, z, mat);
|
|
|
|
x = cos_theta * sin_phi_d_phi;
|
|
y = sin_theta * sin_phi_d_phi;
|
|
z = cos_phi_d_phi;
|
|
DrawShadowVertex (x, y, z, mat);
|
|
}
|
|
x = sin_phi;
|
|
y = 0.0;
|
|
z = cos_phi;
|
|
DrawShadowVertex (x, y, z, mat);
|
|
x = sin_phi_d_phi;
|
|
y = 0.0;
|
|
z = cos_phi_d_phi;
|
|
DrawShadowVertex (x, y, z, mat);
|
|
glEnd();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CCharShape::TraverseDagForShadow (const TCharNode *node, const TMatrix mat) {
|
|
TMatrix new_matrix;
|
|
|
|
MultiplyMatrices (new_matrix, mat, node->trans);
|
|
if (node->visible && node->render_shadow)
|
|
DrawShadowSphere (new_matrix);
|
|
|
|
TCharNode* child = node->child;
|
|
while (child != NULL) {
|
|
TraverseDagForShadow (child, new_matrix);
|
|
child = child->next;
|
|
}
|
|
}
|
|
|
|
void CCharShape::DrawShadow () {
|
|
TMatrix model_matrix;
|
|
|
|
if (g_game.light_id == 1 || g_game.light_id == 3) return;
|
|
|
|
ScopedRenderMode rm(TUX_SHADOW);
|
|
glColor4f (shad_col.r, shad_col.g, shad_col.b, shad_col.a);
|
|
MakeIdentityMatrix (model_matrix);
|
|
|
|
TCharNode *node = GetNode(0);
|
|
if (node == NULL) {
|
|
Message ("couldn't find tux's root node", "");
|
|
return;
|
|
}
|
|
TraverseDagForShadow (node, model_matrix);
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// testing and tools
|
|
// --------------------------------------------------------------------
|
|
|
|
string CCharShape::GetNodeJoint (size_t idx) const {
|
|
if (idx >= numNodes) return "";
|
|
TCharNode *node = Nodes[idx];
|
|
if (node == NULL) return "";
|
|
if (!node->joint.empty()) return node->joint;
|
|
else return Int_StrN ((int)node->node_name);
|
|
}
|
|
|
|
size_t CCharShape::GetNodeName (size_t idx) const {
|
|
if (idx >= numNodes) return -1;
|
|
return Nodes[idx]->node_name;
|
|
}
|
|
|
|
size_t CCharShape::GetNodeName (const string& node_trivialname) const {
|
|
return NodeIndex.at(node_trivialname);
|
|
}
|
|
|
|
|
|
void CCharShape::RefreshNode (size_t idx) {
|
|
if (idx >= numNodes) return;
|
|
TMatrix TempMatrix;
|
|
char caxis;
|
|
double angle;
|
|
|
|
TCharNode *node = Nodes[idx];
|
|
TCharAction *act = node->action;
|
|
if (act == NULL) return;
|
|
if (act->num < 1) return;
|
|
|
|
MakeIdentityMatrix (node->trans);
|
|
MakeIdentityMatrix (node->invtrans);
|
|
|
|
for (size_t i=0; i<act->num; i++) {
|
|
int type = act->type[i];
|
|
const TVector3& vec = act->vec[i];
|
|
double dval = act->dval[i];
|
|
|
|
switch (type) {
|
|
case 0:
|
|
MakeTranslationMatrix (TempMatrix, vec.x, vec.y, vec.z);
|
|
MultiplyMatrices (node->trans, node->trans, TempMatrix);
|
|
MakeTranslationMatrix (TempMatrix, -vec.x, -vec.y, -vec.z);
|
|
MultiplyMatrices (node->invtrans, TempMatrix, node->invtrans);
|
|
break;
|
|
case 1:
|
|
caxis = 'x';
|
|
angle = dval;
|
|
MakeRotationMatrix (TempMatrix, angle, caxis);
|
|
MultiplyMatrices (node->trans, node->trans, TempMatrix);
|
|
MakeRotationMatrix (TempMatrix, -angle, caxis);
|
|
MultiplyMatrices (node->invtrans, TempMatrix, node->invtrans);
|
|
break;
|
|
case 2:
|
|
caxis = 'y';
|
|
angle = dval;
|
|
MakeRotationMatrix (TempMatrix, angle, caxis);
|
|
MultiplyMatrices (node->trans, node->trans, TempMatrix);
|
|
MakeRotationMatrix (TempMatrix, -angle, caxis);
|
|
MultiplyMatrices (node->invtrans, TempMatrix, node->invtrans);
|
|
break;
|
|
case 3:
|
|
caxis = 'z';
|
|
angle = dval;
|
|
MakeRotationMatrix (TempMatrix, angle, caxis);
|
|
MultiplyMatrices (node->trans, node->trans, TempMatrix);
|
|
MakeRotationMatrix (TempMatrix, -angle, caxis);
|
|
MultiplyMatrices (node->invtrans, TempMatrix, node->invtrans);
|
|
break;
|
|
case 4:
|
|
MakeIdentityMatrix (TempMatrix);
|
|
MultiplyMatrices (node->trans, node->trans, TempMatrix);
|
|
MakeIdentityMatrix (TempMatrix);
|
|
MultiplyMatrices (node->invtrans, TempMatrix, node->invtrans);
|
|
|
|
MakeScalingMatrix (TempMatrix, vec.x, vec.y, vec.z);
|
|
MultiplyMatrices (node->trans, node->trans, TempMatrix);
|
|
MakeScalingMatrix (TempMatrix, 1.0 / vec.x, 1.0 / vec.y, 1.0 / vec.z);
|
|
MultiplyMatrices (node->invtrans, TempMatrix, node->invtrans);
|
|
|
|
MakeIdentityMatrix (TempMatrix);
|
|
MultiplyMatrices (node->trans, node->trans, TempMatrix);
|
|
MakeIdentityMatrix (TempMatrix);
|
|
MultiplyMatrices (node->invtrans, TempMatrix, node->invtrans);
|
|
break;
|
|
case 5:
|
|
VisibleNode (node->node_name, dval);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t CCharShape::GetNumNodes () const {
|
|
return numNodes;
|
|
}
|
|
|
|
const string& CCharShape::GetNodeFullname (size_t idx) const {
|
|
if (idx >= numNodes) return emptyString;
|
|
return Nodes[idx]->action->name;
|
|
}
|
|
|
|
size_t CCharShape::GetNumActs (size_t idx) const {
|
|
if (idx >= numNodes) return -1;
|
|
return Nodes[idx]->action->num;
|
|
}
|
|
|
|
TCharAction *CCharShape::GetAction (size_t idx) const {
|
|
if (idx >= numNodes) return NULL;
|
|
return Nodes[idx]->action;
|
|
}
|
|
|
|
void CCharShape::PrintAction (size_t idx) const {
|
|
if (idx >= numNodes) return;
|
|
TCharAction *act = Nodes[idx]->action;
|
|
PrintInt ((int)act->num);
|
|
for (size_t i=0; i<act->num; i++) {
|
|
PrintInt (act->type[i]);
|
|
PrintDouble (act->dval[i]);
|
|
PrintVector (act->vec[i]);
|
|
}
|
|
}
|
|
|
|
void CCharShape::PrintNode (size_t idx) const {
|
|
TCharNode *node = Nodes[idx];
|
|
PrintInt ("node: ", (int)node->node_name);
|
|
PrintInt ("parent: ", (int)node->parent_name);
|
|
PrintInt ("child: ", (int)node->child_name);
|
|
PrintInt ("next: ", (int)node->next_name);
|
|
}
|
|
|
|
void CCharShape::SaveCharNodes (const string& dir, const string& filename) {
|
|
CSPList list (MAX_CHAR_NODES + 10);
|
|
|
|
list.Add ("# Generated by Tuxracer tools");
|
|
list.Add ("");
|
|
if (!Materials.empty()) {
|
|
list.Add ("# Materials:");
|
|
for (size_t i=0; i<Materials.size(); i++)
|
|
if (!Materials[i].matline.empty())
|
|
list.Add (Materials[i].matline);
|
|
list.Add ("");
|
|
}
|
|
|
|
list.Add ("# Nodes:");
|
|
for (size_t i=1; i<numNodes; i++) {
|
|
TCharNode* node = Nodes[i];
|
|
TCharAction* act = node->action;
|
|
if (node->parent_name >= node->node_name) Message ("wrong parent index");
|
|
string line = "*[node] " + Int_StrN ((int)node->node_name);
|
|
line += " [par] " + Int_StrN ((int)node->parent_name);
|
|
bool rotflag = false;
|
|
|
|
if (!act->order.empty()) {
|
|
TVector3 rotation;
|
|
line += " [order] " + act->order;
|
|
for (size_t ii=0; ii<act->order.size(); ii++) {
|
|
int aa = act->order[ii]-48;
|
|
switch (aa) {
|
|
case 0:
|
|
line += " [trans] " + Vector_StrN (act->vec[ii], 2);
|
|
break;
|
|
case 4:
|
|
line += " [scale] " + Vector_StrN (act->vec[ii], 2);
|
|
break;
|
|
case 1:
|
|
rotation.x = act->dval[ii];
|
|
rotflag = true;
|
|
break;
|
|
case 2:
|
|
rotation.y = act->dval[ii];
|
|
rotflag = true;
|
|
break;
|
|
case 3:
|
|
rotation.z = act->dval[ii];
|
|
rotflag = true;
|
|
break;
|
|
case 5:
|
|
line += " [vis] " + Float_StrN (act->dval[ii], 0);
|
|
break;
|
|
case 9:
|
|
rotation.z = act->dval[ii];
|
|
rotflag = true;
|
|
break;
|
|
}
|
|
}
|
|
if (rotflag) line += " [rot] " + Vector_StrN (rotation, 2);
|
|
}
|
|
if (!act->mat.empty()) line += " [mat] " + act->mat;
|
|
if (!node->joint.empty()) line += " [joint] " + node->joint;
|
|
if (!act->name.empty()) line += " [name] " + act->name;
|
|
if (node->render_shadow) line += " [shad] 1";
|
|
|
|
list.Add (line);
|
|
if (i<numNodes-3) {
|
|
if (node->visible && !Nodes[i+1]->visible) list.Add ("");
|
|
const string& joint = Nodes[i+2]->joint;
|
|
if (joint.empty()) list.Add ("# " + joint);
|
|
}
|
|
}
|
|
list.Save (dir, filename);
|
|
}
|