leocad/common/object.cpp

569 lines
14 KiB
C++
Executable file

// Base class for all drawable objects
//
#include "lc_global.h"
#include <stdlib.h>
#include <float.h>
#include <math.h>
#include "globals.h"
#include "project.h"
#include "object.h"
#include "matrix.h"
#include "vector.h"
#include "file.h"
#include "lc_application.h"
#define LC_KEY_SAVE_VERSION 1 // LeoCAD 0.73
// =============================================================================
// Static functions
// Returns in (A,B,C,D) the coefficientes of the plane with the three
// succesive (in counterclockwise order) vertices p1,p2,p3.
static void GetPolyCoeffs (float x1, float y1, float z1, float x2, float y2, float z2,
float x3, float y3, float z3, float *A, float *B, float *C, float *D)
{
*A = ((y1-y2)*(z3-z2)) - ((z1-z2)*(y3-y2));
*B = ((z1-z2)*(x3-x2)) - ((x1-x2)*(z3-z2));
*C = ((x1-x2)*(y3-y2)) - ((y1-y2)*(x3-x2));
*D = - ((*A)*x1) - ((*B)*y1) - ((*C)*z1);
}
// =============================================================================
// ClickLine structure
double LC_CLICKLINE::PointDistance (float *point)
{
Vector op ((float)(point[0] - a1), (float)(point[1] - b1), (float)(point[2] - c1));
Vector d ((float)a2, (float)b2, (float)c2);
float len = d.Length ();
d.Normalize ();
float t = op.Dot (d);
if (t > 0)
{
if (t >= len)
t = 1;
else
t /= len;
d *= (t*len);
op -= d;
}
return op.Length ();
}
// =============================================================================
// Object class
Object::Object (LC_OBJECT_TYPE nType)
{
// m_nState = 0;
// m_strName[0] = '\0';
m_pAnimationKeys = NULL;
m_pInstructionKeys = NULL;
m_nObjectType = nType;
m_pKeyValues = NULL;
// m_pParent = NULL;
// m_pNext = NULL;
// m_pNextRender = NULL;
}
Object::~Object ()
{
delete []m_pKeyValues;
RemoveKeys ();
}
bool Object::FileLoad (File& file)
{
lcuint8 version;
file.ReadByte (&version, 1);
if (version > LC_KEY_SAVE_VERSION)
return false;
lcuint16 time;
float param[4];
lcuint8 type;
lcuint32 n;
file.ReadLong (&n, 1);
while (n--)
{
file.ReadShort (&time, 1);
file.ReadFloat (param, 4);
file.ReadByte (&type, 1);
ChangeKey (time, false, true, param, type);
}
file.ReadLong (&n, 1);
while (n--)
{
file.ReadShort (&time, 1);
file.ReadFloat (param, 4);
file.ReadByte (&type, 1);
ChangeKey (time, true, true, param, type);
}
return true;
}
void Object::FileSave (File& file) const
{
lcuint8 version = LC_KEY_SAVE_VERSION;
LC_OBJECT_KEY *node;
lcuint32 n;
file.WriteByte (&version, 1);
for (n = 0, node = m_pInstructionKeys; node; node = node->next)
n++;
file.WriteLong (&n, 1);
for (node = m_pInstructionKeys; node; node = node->next)
{
file.WriteShort (&node->time, 1);
file.WriteFloat (node->param, 4);
file.WriteByte (&node->type, 1);
}
for (n = 0, node = m_pAnimationKeys; node; node = node->next)
n++;
file.WriteLong (&n, 1);
for (node = m_pAnimationKeys; node; node = node->next)
{
file.WriteShort (&node->time, 1);
file.WriteFloat (node->param, 4);
file.WriteByte (&node->type, 1);
}
}
// =============================================================================
// Key handling
static LC_OBJECT_KEY* AddNode (LC_OBJECT_KEY *node, unsigned short nTime, unsigned char nType)
{
LC_OBJECT_KEY* newnode = (LC_OBJECT_KEY*)malloc (sizeof (LC_OBJECT_KEY));
if (node)
{
newnode->next = node->next;
node->next = newnode;
}
else
newnode->next = NULL;
newnode->type = nType;
newnode->time = nTime;
newnode->param[0] = newnode->param[1] = newnode->param[2] = newnode->param[3] = 0;
return newnode;
}
void Object::RegisterKeys (float *values[], LC_OBJECT_KEY_INFO* info, int count)
{
int i;
m_pKeyValues = new float* [count];
for (i = 0; i < count; i++)
m_pKeyValues[i] = values[i];
m_pAnimationKeys = AddNode (NULL, 1, 0);
m_pInstructionKeys = AddNode (NULL, 1, 0);
for (i = count-1; i > 0; i--)
{
AddNode (m_pAnimationKeys, 1, i);
AddNode (m_pInstructionKeys, 1, i);
}
m_pKeyInfo = info;
m_nKeyInfoCount = count;
}
void Object::RemoveKeys ()
{
LC_OBJECT_KEY *node, *prev;
for (node = m_pInstructionKeys; node;)
{
prev = node;
node = node->next;
free (prev);
}
for (node = m_pAnimationKeys; node;)
{
prev = node;
node = node->next;
free (prev);
}
}
void Object::ChangeKey (unsigned short nTime, bool bAnimation, bool bAddKey, const float *param, unsigned char nKeyType)
{
LC_OBJECT_KEY *node, *poskey = NULL, *newpos = NULL;
if (bAnimation)
node = m_pAnimationKeys;
else
node = m_pInstructionKeys;
while (node)
{
if ((node->time <= nTime) &&
(node->type == nKeyType))
poskey = node;
node = node->next;
}
if (bAddKey)
{
if (poskey)
{
if (poskey->time != nTime)
newpos = AddNode(poskey, nTime, nKeyType);
}
else
newpos = AddNode(poskey, nTime, nKeyType);
}
if (newpos == NULL)
newpos = poskey;
for (int i = 0; i < m_pKeyInfo[nKeyType].size; i++)
newpos->param[i] = param[i];
}
void Object::CalculateKeys (unsigned short nTime, bool bAnimation)
{
// LC_OBJECT_KEY *next[m_nKeyInfoCount], *prev[m_nKeyInfoCount], *node;
LC_OBJECT_KEY *next[32], *prev[32], *node;
int i, empty = m_nKeyInfoCount;
for (i = 0; i < m_nKeyInfoCount; i++)
next[i] = NULL;
if (bAnimation)
node = m_pAnimationKeys;
else
node = m_pInstructionKeys;
// Get the previous and next keys for each variable
while (node && empty)
{
if (node->time <= nTime)
{
prev[node->type] = node;
}
else
{
if (next[node->type] == NULL)
{
next[node->type] = node;
empty--;
}
}
node = node->next;
}
// TODO: USE KEY IN/OUT WEIGHTS
for (i = 0; i < m_nKeyInfoCount; i++)
{
LC_OBJECT_KEY *n = next[i], *p = prev[i];
if (bAnimation && (n != NULL) && (p->time != nTime))
{
float t = (float)(nTime - p->time)/(n->time - p->time);
for (int j = 0; j < m_pKeyInfo[i].size; j++)
m_pKeyValues[i][j] = p->param[j] + (n->param[j] - p->param[j])*t;
}
else
for (int j = 0; j < m_pKeyInfo[i].size; j++)
m_pKeyValues[i][j] = p->param[j];
}
}
void Object::CalculateSingleKey (unsigned short nTime, bool bAnimation, int keytype, float *value) const
{
LC_OBJECT_KEY *next = NULL, *prev = NULL, *node;
if (bAnimation)
node = m_pAnimationKeys;
else
node = m_pInstructionKeys;
while (node)
{
if (node->type == keytype)
{
if (node->time <= nTime)
prev = node;
else
{
if (next == NULL)
{
next = node;
break;
}
}
}
node = node->next;
}
// TODO: USE KEY IN/OUT WEIGHTS
if (bAnimation && (next != NULL) && (prev->time != nTime))
{
float t = (float)(nTime - prev->time)/(next->time - prev->time);
for (int j = 0; j < m_pKeyInfo[keytype].size; j++)
value[j] = prev->param[j] + (next->param[j] - prev->param[j])*t;
}
else
for (int j = 0; j < m_pKeyInfo[keytype].size; j++)
value[j] = prev->param[j];
}
void Object::InsertTime (unsigned short start, bool animation, unsigned short time)
{
LC_OBJECT_KEY *node, *prev = NULL;
unsigned short last;
bool end[32];
int i;
for (i = 0; i < m_nKeyInfoCount; i++)
end[i] = false;
if (animation)
{
node = m_pAnimationKeys;
last = lcGetActiveProject()->GetTotalFrames ();
}
else
{
node = m_pInstructionKeys;
last = 255;
}
for (; node != NULL; prev = node, node = node->next)
{
// skip everything before the start time
if ((node->time < start) || (node->time == 1))
continue;
// there's already a key at the end, delete this one
if (end[node->type])
{
prev->next = node->next;
free (node);
node = prev;
continue;
}
node->time += time;
if (node->time >= last)
{
node->time = last;
end[node->type] = true;
}
}
}
void Object::RemoveTime (unsigned short start, bool animation, unsigned short time)
{
LC_OBJECT_KEY *node, *prev = NULL;
if (animation)
node = m_pAnimationKeys;
else
node = m_pInstructionKeys;
for (; node != NULL; prev = node, node = node->next)
{
// skip everything before the start time
if ((node->time < start) || (node->time == 1))
continue;
if (node->time < (start + time))
{
// delete this key
prev->next = node->next;
free (node);
node = prev;
continue;
}
node->time -= time;
if (node->time < 1)
node->time = 1;
}
}
// =============================================================================
// BoundingBox stuff
// Find the distance from the object to the beginning of the "click line".
double Object::BoundingBoxIntersectDist (LC_CLICKLINE* pLine) const
{
double x, y, z;
if (BoundingBoxIntersectionbyLine (pLine->a1, pLine->b1, pLine->c1, pLine->a2, pLine->b2, pLine->c2, &x, &y, &z))
return (float)sqrt ((pLine->a1-x)*(pLine->a1-x)+(pLine->b1-y)*(pLine->b1-y)+(pLine->c1-z)*(pLine->c1-z));
return DBL_MAX;
}
// Returns TRUE if the specified point is inside the bounding box of this object.
bool Object::BoundingBoxPointInside(double x, double y, double z) const
{
int i = 0;
while (i < 6 && ((m_fBoxPlanes[0][i]*x + m_fBoxPlanes[1][i]*y +
m_fBoxPlanes[2][i]*z + m_fBoxPlanes[3][i]) <= 0.001))
i++;
return (i == 6);
}
// Returns TRUE if the line is intersecting any of the planes of the bounding
// box and if this point is also inside this bounding box.
bool Object::BoundingBoxIntersectionbyLine (double a1, double b1, double c1, double a2, double b2,
double c2, double *x, double *y, double *z) const
{
double curr_t = DBL_MAX;
double t, t1, t2;
for (int i = 0; i < 6; i++)
{
t1 = (m_fBoxPlanes[0][i]*a1 + m_fBoxPlanes[1][i]*b1 + m_fBoxPlanes[2][i]*c1 + m_fBoxPlanes[3][i]);
t2 = (m_fBoxPlanes[0][i]*a2 + m_fBoxPlanes[1][i]*b2 + m_fBoxPlanes[2][i]*c2);
if (t1!=0 && t2!=0)
{
t = -( t1 / t2 );
if (t>=0)
{
*x=a1+a2*t;
*y=b1+b2*t;
*z=c1+c2*t;
if (BoundingBoxPointInside(*x,*y,*z))
if (t < curr_t)
curr_t = t;
}
}
}
if (curr_t != DBL_MAX)
{
*x=a1+a2*curr_t;
*y=b1+b2*curr_t;
*z=c1+c2*curr_t;
return true;
}
else
return false;
}
// For pieces
void Object::BoundingBoxCalculate (Matrix *mat, float Dimensions[6])
{
// BASE TOP
// 1------3 .------4 ^ X
// | | | | |
// | | | | | Y
// 0------. 2------5 .--->
float pts[18] = {
Dimensions[0], Dimensions[1], Dimensions[5],
Dimensions[3], Dimensions[1], Dimensions[5],
Dimensions[0], Dimensions[1], Dimensions[2],
Dimensions[3], Dimensions[4], Dimensions[5],
Dimensions[3], Dimensions[4], Dimensions[2],
Dimensions[0], Dimensions[4], Dimensions[2] };
mat->TransformPoints(pts, 6);
GetPolyCoeffs (pts[3], pts[4], pts[5], pts[0], pts[1], pts[2], pts[6], pts[7], pts[8],
&m_fBoxPlanes[0][0], &m_fBoxPlanes[1][0], &m_fBoxPlanes[2][0], &m_fBoxPlanes[3][0]); //1,0,2
GetPolyCoeffs (pts[9], pts[10],pts[11], pts[12],pts[13],pts[14], pts[15],pts[16],pts[17],
&m_fBoxPlanes[0][1], &m_fBoxPlanes[1][1], &m_fBoxPlanes[2][1], &m_fBoxPlanes[3][1]); //3,4,5
GetPolyCoeffs (pts[15],pts[16],pts[17], pts[6], pts[7], pts[8], pts[0], pts[1], pts[2],
&m_fBoxPlanes[0][2], &m_fBoxPlanes[1][2], &m_fBoxPlanes[2][2], &m_fBoxPlanes[3][2]); //5,2,0
GetPolyCoeffs (pts[12],pts[13],pts[14], pts[9], pts[10],pts[11], pts[3], pts[4], pts[5],
&m_fBoxPlanes[0][3], &m_fBoxPlanes[1][3], &m_fBoxPlanes[2][3], &m_fBoxPlanes[3][3]); //4,3,1
GetPolyCoeffs (pts[6], pts[7], pts[8], pts[15],pts[16],pts[17], pts[12],pts[13],pts[14],
&m_fBoxPlanes[0][4], &m_fBoxPlanes[1][4], &m_fBoxPlanes[2][4], &m_fBoxPlanes[3][4]); //2,5,4
GetPolyCoeffs (pts[0], pts[1], pts[2], pts[3], pts[4], pts[5], pts[9], pts[10],pts[11],
&m_fBoxPlanes[0][5], &m_fBoxPlanes[1][5], &m_fBoxPlanes[2][5], &m_fBoxPlanes[3][5]); //0,1,3
}
// Cameras
void Object::BoundingBoxCalculate (Matrix *mat)
{
float normals[6][3] = {
{ 1,0,0 }, { 0,1,0 }, { 0,0,1 },
{ -1,0,0 }, { 0,-1,0 }, { 0,0,-1 } };
float x,y,z,dist;
if (IsCamera ())
dist = 0.3f;
else
dist = 0.2f;
mat->GetTranslation(&x,&y,&z);
mat->SetTranslation(0,0,0);
mat->TransformPoints(&normals[0][0], 6);
for (int i = 0; i < 6; i++)
{
m_fBoxPlanes[0][i] = normals[i][0];
m_fBoxPlanes[1][i] = normals[i][1];
m_fBoxPlanes[2][i] = normals[i][2];
float pt[3];
pt[0] = dist*normals[i][0] + x;
pt[1] = dist*normals[i][1] + y;
pt[2] = dist*normals[i][2] + z;
m_fBoxPlanes[3][i] = -(pt[0]*normals[i][0]+pt[1]*normals[i][1]+pt[2]*normals[i][2]);
}
}
// Light
void Object::BoundingBoxCalculate (float pos[3])
{
float pts[18] = {
0.3f+pos[0], 0.3f+pos[1], -0.3f+pos[2],
-0.3f+pos[0], 0.3f+pos[1], -0.3f+pos[2],
0.3f+pos[0], 0.3f+pos[1], 0.3f+pos[2],
-0.3f+pos[0], -0.3f+pos[1], -0.3f+pos[2],
-0.3f+pos[0], -0.3f+pos[1], 0.3f+pos[2],
0.3f+pos[0], -0.3f+pos[1], 0.3f+pos[2] };
GetPolyCoeffs (pts[3], pts[4], pts[5], pts[0], pts[1], pts[2], pts[6], pts[7], pts[8],
&m_fBoxPlanes[0][0], &m_fBoxPlanes[1][0], &m_fBoxPlanes[2][0], &m_fBoxPlanes[3][0]); //1,0,2
GetPolyCoeffs (pts[9], pts[10],pts[11], pts[12],pts[13],pts[14], pts[15],pts[16],pts[17],
&m_fBoxPlanes[0][1], &m_fBoxPlanes[1][1], &m_fBoxPlanes[2][1], &m_fBoxPlanes[3][1]); //3,4,5
GetPolyCoeffs (pts[15],pts[16],pts[17], pts[6], pts[7], pts[8], pts[0], pts[1], pts[2],
&m_fBoxPlanes[0][2], &m_fBoxPlanes[1][2], &m_fBoxPlanes[2][2], &m_fBoxPlanes[3][2]); //5,2,0
GetPolyCoeffs (pts[12],pts[13],pts[14], pts[9], pts[10],pts[11], pts[3], pts[4], pts[5],
&m_fBoxPlanes[0][3], &m_fBoxPlanes[1][3], &m_fBoxPlanes[2][3], &m_fBoxPlanes[3][3]); //4,3,1
GetPolyCoeffs (pts[6], pts[7], pts[8], pts[15],pts[16],pts[17], pts[12],pts[13],pts[14],
&m_fBoxPlanes[0][4], &m_fBoxPlanes[1][4], &m_fBoxPlanes[2][4], &m_fBoxPlanes[3][4]); //2,5,4
GetPolyCoeffs (pts[0], pts[1], pts[2], pts[3], pts[4], pts[5], pts[9], pts[10],pts[11],
&m_fBoxPlanes[0][5], &m_fBoxPlanes[1][5], &m_fBoxPlanes[2][5], &m_fBoxPlanes[3][5]); //0,1,3
}