summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/Makefile1
-rw-r--r--common/boundbox.cpp182
-rw-r--r--common/boundbox.h46
-rw-r--r--common/camera.cpp1041
-rw-r--r--common/camera.h135
-rw-r--r--common/defines.h180
-rw-r--r--common/file.cpp337
-rw-r--r--common/file.h63
-rw-r--r--common/globals.cpp87
-rw-r--r--common/globals.h12
-rw-r--r--common/group.cpp79
-rw-r--r--common/group.h31
-rw-r--r--common/image.cpp1608
-rw-r--r--common/image.h14
-rw-r--r--common/light.cpp382
-rw-r--r--common/light.h53
-rw-r--r--common/matrix.cpp643
-rw-r--r--common/matrix.h45
-rw-r--r--common/module.mk3
-rw-r--r--common/piece.cpp1771
-rw-r--r--common/piece.h186
-rw-r--r--common/pieceinf.cpp1818
-rw-r--r--common/pieceinf.h92
-rw-r--r--common/project.cpp7063
-rw-r--r--common/project.h255
-rw-r--r--common/quant.cpp639
-rw-r--r--common/quant.h31
-rw-r--r--common/terrain.cpp855
-rw-r--r--common/terrain.h92
-rw-r--r--common/texture.cpp183
-rw-r--r--common/texture.h51
-rw-r--r--common/tr.cpp325
-rw-r--r--common/tr.h75
-rw-r--r--common/typedefs.h318
-rw-r--r--common/vector.cpp178
-rw-r--r--common/vector.h43
36 files changed, 18917 insertions, 0 deletions
diff --git a/common/Makefile b/common/Makefile
new file mode 100644
index 0000000..05788fd
--- /dev/null
+++ b/common/Makefile
@@ -0,0 +1 @@
+include ../generic.mk
diff --git a/common/boundbox.cpp b/common/boundbox.cpp
new file mode 100644
index 0000000..6dc6d17
--- /dev/null
+++ b/common/boundbox.cpp
@@ -0,0 +1,182 @@
+// Simple bounding box.
+//
+
+#include <float.h>
+#include <math.h>
+#include "boundbox.h"
+#include "matrix.h"
+#include "defines.h"
+
+// 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);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// BoundingBox construction/destruction
+
+BoundingBox::BoundingBox()
+{
+
+}
+
+BoundingBox::~BoundingBox()
+{
+
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// BoundingBox implementation
+
+void BoundingBox::Initialize(void* pOwner, unsigned char nType)
+{
+ m_pOwner = pOwner;
+ m_nParentType = nType;
+}
+
+// Find the distance from the object to the beginning of the "click line".
+double BoundingBox::FindIntersectDist(CLICKLINE* pLine)
+{
+ double x, y, z;
+
+ if (IntersectionbyLine(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;
+}
+
+// For pieces
+void BoundingBox::CalculateBoundingBox(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_fPlanes[0][0], &m_fPlanes[1][0], &m_fPlanes[2][0], &m_fPlanes[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_fPlanes[0][1], &m_fPlanes[1][1], &m_fPlanes[2][1], &m_fPlanes[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_fPlanes[0][2], &m_fPlanes[1][2], &m_fPlanes[2][2], &m_fPlanes[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_fPlanes[0][3], &m_fPlanes[1][3], &m_fPlanes[2][3], &m_fPlanes[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_fPlanes[0][4], &m_fPlanes[1][4], &m_fPlanes[2][4], &m_fPlanes[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_fPlanes[0][5], &m_fPlanes[1][5], &m_fPlanes[2][5], &m_fPlanes[3][5]); // (0,1,3)
+}
+
+// Cameras
+void BoundingBox::CalculateBoundingBox(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 (m_nParentType == LC_CAMERA)
+ 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_fPlanes[0][i] = normals[i][0];
+ m_fPlanes[1][i] = normals[i][1];
+ m_fPlanes[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_fPlanes[3][i] = -(pt[0]*normals[i][0]+pt[1]*normals[i][1]+pt[2]*normals[i][2]);
+ }
+}
+
+// Light
+void BoundingBox::CalculateBoundingBox(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_fPlanes[0][0], &m_fPlanes[1][0], &m_fPlanes[2][0], &m_fPlanes[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_fPlanes[0][1], &m_fPlanes[1][1], &m_fPlanes[2][1], &m_fPlanes[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_fPlanes[0][2], &m_fPlanes[1][2], &m_fPlanes[2][2], &m_fPlanes[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_fPlanes[0][3], &m_fPlanes[1][3], &m_fPlanes[2][3], &m_fPlanes[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_fPlanes[0][4], &m_fPlanes[1][4], &m_fPlanes[2][4], &m_fPlanes[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_fPlanes[0][5], &m_fPlanes[1][5], &m_fPlanes[2][5], &m_fPlanes[3][5]); // (0,1,3)
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// BoundingBox helpers
+
+// Returns TRUE if the specified point is inside the bounding box of this object.
+bool BoundingBox::PointInside(double x, double y, double z)
+{
+ int i = 0;
+ while (i < 6 && ((m_fPlanes[0][i]*x + m_fPlanes[1][i]*y +
+ m_fPlanes[2][i]*z + m_fPlanes[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 BoundingBox::IntersectionbyLine(double a1, double b1, double c1, double a2, double b2, double c2, double *x, double *y, double *z)
+{
+ double curr_t = DBL_MAX;
+ double t, t1, t2;
+
+ for (int i = 0; i < 6; i++)
+ {
+ t1 = (m_fPlanes[0][i]*a1 + m_fPlanes[1][i]*b1 + m_fPlanes[2][i]*c1 + m_fPlanes[3][i]);
+ t2 = (m_fPlanes[0][i]*a2 + m_fPlanes[1][i]*b2 + m_fPlanes[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 (PointInside(*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;
+}
diff --git a/common/boundbox.h b/common/boundbox.h
new file mode 100644
index 0000000..5354396
--- /dev/null
+++ b/common/boundbox.h
@@ -0,0 +1,46 @@
+//
+// boundbox.h
+////////////////////////////////////////////////////
+
+#ifndef _BOUNDBOX_H_
+#define _BOUNDBOX_H_
+
+class Matrix;
+class BoundingBox;
+
+// Callback "closure" struct, used to make the necessary parameters known to
+// the callback function.
+typedef struct {
+ double a1,b1,c1;
+ double a2,b2,c2;
+ double mindist;
+ BoundingBox *pClosest;
+} CLICKLINE;
+
+class BoundingBox
+{
+public:
+ BoundingBox();
+ ~BoundingBox();
+
+ int GetOwnerType()
+ { return m_nParentType; }
+ void* GetOwner()
+ { return m_pOwner; }
+
+ void Initialize(void* pOwner, unsigned char nType);
+ double FindIntersectDist(CLICKLINE* pLine);
+ void CalculateBoundingBox(float pos[3]);
+ void CalculateBoundingBox(Matrix *mat);
+ void CalculateBoundingBox(Matrix *mat, float Dimensions[6]);
+
+protected:
+ void* m_pOwner;
+ unsigned char m_nParentType;
+ float m_fPlanes[4][6];
+
+ bool IntersectionbyLine(double a1, double b1, double c1, double a2, double b2, double c2, double *x, double *y, double *z);
+ bool PointInside(double x, double y, double z);
+};
+
+#endif // _BOUNDBOX_H_
diff --git a/common/camera.cpp b/common/camera.cpp
new file mode 100644
index 0000000..03791ec
--- /dev/null
+++ b/common/camera.cpp
@@ -0,0 +1,1041 @@
+// Camera object.
+
+#ifdef _WINDOWS
+#include "stdafx.h"
+#else
+#include "GL/glu.h"
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "globals.h"
+#include "defines.h"
+#include "vector.h"
+#include "matrix.h"
+#include "file.h"
+#include "boundbox.h"
+#include "camera.h"
+#include "tr.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// Static functions
+
+static GLuint _nTargetList = 0;
+
+static CAMERA_KEY* AddNode (CAMERA_KEY *node, unsigned short nTime, unsigned char nType)
+{
+ CAMERA_KEY* newnode = (CAMERA_KEY*)malloc(sizeof(CAMERA_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] = 0;
+
+ return newnode;
+}
+
+static bool invert(double src[16], double inverse[16])
+{
+ double t;
+ int i, j, k, swap;
+ double tmp[4][4];
+
+ for (i = 0; i < 16; i++)
+ inverse[i] = 0.0;
+ inverse[0] = inverse[5] = inverse[10] = inverse[15] = 1.0;
+
+ for (i = 0; i < 4; i++)
+ for (j = 0; j < 4; j++)
+ tmp[i][j] = src[i*4+j];
+
+ for (i = 0; i < 4; i++)
+ {
+ // look for largest element in column.
+ swap = i;
+ for (j = i + 1; j < 4; j++)
+ if (fabs(tmp[j][i]) > fabs(tmp[i][i]))
+ swap = j;
+
+ if (swap != i)
+ {
+ // swap rows.
+ for (k = 0; k < 4; k++)
+ {
+ t = tmp[i][k];
+ tmp[i][k] = tmp[swap][k];
+ tmp[swap][k] = t;
+
+ t = inverse[i*4+k];
+ inverse[i*4+k] = inverse[swap*4+k];
+ inverse[swap*4+k] = t;
+ }
+ }
+
+ if (tmp[i][i] == 0)
+ {
+ // The matrix is singular, which shouldn't happen.
+ return false;
+ }
+
+ t = tmp[i][i];
+ for (k = 0; k < 4; k++)
+ {
+ tmp[i][k] /= t;
+ inverse[i*4+k] /= t;
+ }
+ for (j = 0; j < 4; j++)
+ {
+ if (j != i)
+ {
+ t = tmp[j][i];
+ for (k = 0; k < 4; k++)
+ {
+ tmp[j][k] -= tmp[i][k]*t;
+ inverse[j*4+k] -= inverse[i*4+k]*t;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Camera construction/destruction
+
+Camera::Camera()
+{
+ Initialize();
+}
+
+// Start with a standard camera.
+Camera::Camera(int nType, Camera* pPrev)
+{
+ if (nType > 7)
+ nType = 8;
+
+ char names[8][7] = { "Front", "Back", "Top", "Under", "Left", "Right", "Main", "User" };
+ float eyes[8][3] = { 50,0,0, -50,0,0, 0,0,50, 0,0,-50, 0,50,0, 0,-50,0, 10,10,5, 0,5,0 };
+ float ups [8][3] = { 0,0,1, 0,0,1, 1,0, 0, -1,0, 0, 0,0,1, 0, 0,1, -0.2357f, -0.2357f, 0.94281f, 0,0,1 };
+ CAMERA_KEY* node;
+
+ Initialize();
+ m_pAnimationKeys = AddNode (NULL, 1, CK_EYE);
+ m_pAnimationKeys->param[0] = eyes[nType][0];
+ m_pAnimationKeys->param[1] = eyes[nType][1];
+ m_pAnimationKeys->param[2] = eyes[nType][2];
+ node = AddNode (m_pAnimationKeys, 1, CK_TARGET);
+ node = AddNode (node, 1, CK_UP);
+ node->param[0] = ups[nType][0];
+ node->param[1] = ups[nType][1];
+ node->param[2] = ups[nType][2];
+
+ m_pInstructionKeys = AddNode (NULL, 1, CK_EYE);
+ m_pInstructionKeys->param[0] = eyes[nType][0];
+ m_pInstructionKeys->param[1] = eyes[nType][1];
+ m_pInstructionKeys->param[2] = eyes[nType][2];
+ node = AddNode (m_pInstructionKeys, 1, CK_TARGET);
+ node = AddNode (node, 1, CK_UP);
+ node->param[0] = ups[nType][0];
+ node->param[1] = ups[nType][1];
+ node->param[2] = ups[nType][2];
+
+ strcpy (m_strName, names[nType]);
+ if (nType != 8)
+ m_nState = LC_CAMERA_HIDDEN;
+ m_nType = nType;
+
+ if (pPrev)
+ pPrev->m_pNext = this;
+
+ UpdatePosition(1, false);
+}
+
+// From OnMouseMove(), case LC_ACTION_ROTATE_VIEW
+Camera::Camera(float eye[3], float target[3], float up[3], Camera* pCamera)
+{
+ CAMERA_KEY* node;
+
+ // Fix the up vector
+ Vector upvec(up), frontvec(eye[0]-target[0], eye[1]-target[1], eye[2]-target[2]), sidevec;
+ frontvec.Normalize();
+ sidevec.Cross(frontvec, upvec);
+ upvec.Cross(sidevec, frontvec);
+ upvec.Normalize();
+
+ Initialize();
+ m_pAnimationKeys = AddNode (NULL, 1, CK_EYE);
+ m_pAnimationKeys->param[0] = eye[0];
+ m_pAnimationKeys->param[1] = eye[1];
+ m_pAnimationKeys->param[2] = eye[2];
+ node = AddNode (m_pAnimationKeys, 1, CK_TARGET);
+ node->param[0] = target[0];
+ node->param[1] = target[1];
+ node->param[2] = target[2];
+ node = AddNode (node, 1, CK_UP);
+ node->param[0] = upvec.X();
+ node->param[1] = upvec.Y();
+ node->param[2] = upvec.Z();
+
+ m_pInstructionKeys = AddNode (NULL, 1, CK_EYE);
+ m_pInstructionKeys->param[0] = eye[0];
+ m_pInstructionKeys->param[1] = eye[1];
+ m_pInstructionKeys->param[2] = eye[2];
+ node = AddNode (m_pInstructionKeys, 1, CK_TARGET);
+ node->param[0] = target[0];
+ node->param[1] = target[1];
+ node->param[2] = target[2];
+ node = AddNode (node, 1, CK_UP);
+ node->param[0] = upvec.X();
+ node->param[1] = upvec.Y();
+ node->param[2] = upvec.Z();
+
+ int i, max = 0;
+
+ for (;;)
+ {
+ if (strncmp (pCamera->m_strName, "Camera ", 7) == 0)
+ if (sscanf(pCamera->m_strName, "Camera %d", &i) == 1)
+ if (i > max)
+ max = i;
+
+ if (pCamera->m_pNext == NULL)
+ {
+ sprintf(m_strName, "Camera %d", max+1);
+ pCamera->m_pNext = this;
+ break;
+ }
+ else
+ pCamera = pCamera->m_pNext;
+ }
+
+ UpdatePosition(1, false);
+}
+
+// From LC_ACTION_CAMERA
+Camera::Camera(float ex, float ey, float ez, float tx, float ty, float tz, Camera* pCamera)
+{
+ CAMERA_KEY* node;
+
+ // Fix the up vector
+ Vector upvec(0,0,1), frontvec(ex-tx, ey-ty, ez-tz), sidevec;
+ frontvec.Normalize();
+ if (frontvec == upvec)
+ sidevec.FromFloat(1,0,0);
+ else
+ sidevec.Cross(frontvec, upvec);
+ upvec.Cross(sidevec, frontvec);
+ upvec.Normalize();
+
+ Initialize();
+ m_pAnimationKeys = AddNode (NULL, 1, CK_EYE);
+ m_pAnimationKeys->param[0] = ex;
+ m_pAnimationKeys->param[1] = ey;
+ m_pAnimationKeys->param[2] = ez;
+ node = AddNode (m_pAnimationKeys, 1, CK_TARGET);
+ node->param[0] = tx;
+ node->param[1] = ty;
+ node->param[2] = tz;
+ node = AddNode (node, 1, CK_UP);
+ node->param[0] = upvec.X();
+ node->param[1] = upvec.Y();
+ node->param[2] = upvec.Z();
+
+ m_pInstructionKeys = AddNode (NULL, 1, CK_EYE);
+ m_pInstructionKeys->param[0] = ex;
+ m_pInstructionKeys->param[1] = ey;
+ m_pInstructionKeys->param[2] = ez;
+ node = AddNode (m_pInstructionKeys, 1, CK_TARGET);
+ node->param[0] = tx;
+ node->param[1] = ty;
+ node->param[2] = tz;
+ node = AddNode (node, 1, CK_UP);
+ node->param[0] = upvec.X();
+ node->param[1] = upvec.Y();
+ node->param[2] = upvec.Z();
+
+ int i, max = 0;
+
+ if (pCamera)
+ for (;;)
+ {
+ if (strncmp (pCamera->m_strName, "Camera ", 7) == 0)
+ if (sscanf(pCamera->m_strName, "Camera %d", &i) == 1)
+ if (i > max)
+ max = i;
+
+ if (pCamera->m_pNext == NULL)
+ {
+ sprintf(m_strName, "Camera %d", max+1);
+ pCamera->m_pNext = this;
+ break;
+ }
+ else
+ pCamera = pCamera->m_pNext;
+ }
+
+ UpdatePosition(1, false);
+}
+
+Camera::~Camera()
+{
+ RemoveKeys();
+}
+
+void Camera::Initialize()
+{
+ m_fovy = 30;
+ m_zNear = 1;
+ m_zFar = 100;
+
+ m_BoundingBox.Initialize(this, LC_CAMERA);
+ m_TargetBoundingBox.Initialize(this, LC_CAMERA_TARGET);
+ m_pNext = NULL;
+ m_nState = 0;
+ m_pAnimationKeys = NULL;
+ m_pInstructionKeys = NULL;
+ m_nList = 0;
+ m_nType = LC_CAMERA_USER;
+
+ m_pTR = NULL;
+ for( int i = 0 ; i < sizeof(m_strName) ; i++ )
+ {
+ m_strName[i] = 0;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Camera save/load
+
+void Camera::FileLoad(File* file)
+{
+ RemoveKeys();
+ unsigned char version, ch;
+ CAMERA_KEY *node;
+
+ file->Read(&version, 1);
+
+ if (version == 4)
+ {
+ file->Read(m_strName, 80);
+ m_strName[80] = 0;
+ }
+ else
+ {
+ file->Read(&ch, 1);
+ if (ch == 0xFF)
+ return; // don't read CString
+ file->Read(m_strName, ch);
+ m_strName[ch] = 0;
+ }
+
+ if (version < 3)
+ {
+ double d[3];
+ file->Read(d, sizeof(d));
+ m_pInstructionKeys = AddNode(NULL, 1, CK_EYE);
+ m_pInstructionKeys->param[0] = (float)d[0];
+ m_pInstructionKeys->param[1] = (float)d[1];
+ m_pInstructionKeys->param[2] = (float)d[2];
+ file->Read(d, sizeof(d));
+ node = AddNode(m_pInstructionKeys, 1, CK_TARGET);
+ node->param[0] = (float)d[0];
+ node->param[1] = (float)d[1];
+ node->param[2] = (float)d[2];
+ file->Read (d, sizeof(d));
+ node = AddNode(node, 1, CK_UP);
+ node->param[0] = (float)d[0];
+ node->param[1] = (float)d[1];
+ node->param[2] = (float)d[2];
+// m_Start.snapshot = FALSE;
+ }
+
+ if (version == 3)
+ {
+ file->Read(&ch, 1);
+
+ node = NULL;
+ while (ch--)
+ {
+ if (node == NULL)
+ {
+ m_pInstructionKeys = AddNode(NULL, 1, CK_EYE);
+ node = m_pInstructionKeys;
+ }
+ else
+ node = AddNode(node, 1, CK_EYE);
+
+ unsigned char step;
+ double eye[3], target[3], up[3];
+ file->Read(eye, sizeof(double[3]));
+ file->Read(target, sizeof(double[3]));
+ file->Read(up, sizeof(double[3]));
+ file->Read(&step, 1);
+
+ if (up[0] == 0 && up[1] == 0 && up[2] == 0)
+ up[2] = 1;
+
+ node->time = step;
+ node->param[0] = (float)eye[0];
+ node->param[1] = (float)eye[1];
+ node->param[2] = (float)eye[2];
+ node = AddNode(node, step, CK_TARGET);
+ node->param[0] = (float)target[0];
+ node->param[1] = (float)target[1];
+ node->param[2] = (float)target[2];
+ node = AddNode(node, step, CK_UP);
+ node->param[0] = (float)up[0];
+ node->param[1] = (float)up[1];
+ node->param[2] = (float)up[2];
+
+ int snapshot; // BOOL under Windows
+ int cam;
+ file->Read(&snapshot, 4);
+ file->Read(&cam, 4);
+// if (cam == -1)
+// node->pCam = NULL;
+// else
+// node->pCam = pDoc->GetCamera(i);
+ }
+ }
+
+ if (version < 4)
+ {
+ m_pAnimationKeys = AddNode (NULL, 1, CK_EYE);
+ CAMERA_KEY* node = AddNode (m_pAnimationKeys, 1, CK_TARGET);
+ node = AddNode (node, 1, CK_UP);
+ memcpy(m_pAnimationKeys->param, m_pInstructionKeys->param, sizeof(float[3]));
+ memcpy(m_pAnimationKeys->next->param, m_pInstructionKeys->next->param, sizeof(float[3]));
+ memcpy(node->param, m_pInstructionKeys->next->next->param, sizeof(float[3]));
+
+ double d;
+ file->Read(&d, sizeof(d)); m_fovy = (float)d;
+ file->Read(&d, sizeof(d)); m_zFar = (float)d;
+ file->Read(&d, sizeof(d)); m_zNear= (float)d;
+ }
+ else
+ {
+ int n;
+
+ file->Read(&n, 4);
+ for (node = NULL; n--;)
+ {
+ if (node == NULL)
+ {
+ m_pInstructionKeys = AddNode(NULL, 1, CK_EYE);
+ node = m_pInstructionKeys;
+ }
+ else
+ node = AddNode(node, 1, CK_EYE);
+ file->Read(&node->time, 2);
+ file->Read(node->param, 12);
+ file->Read(&node->type, 1);
+ }
+
+ file->Read(&n, 4);
+ for (node = NULL; n--;)
+ {
+ if (node == NULL)
+ {
+ m_pAnimationKeys = AddNode(NULL, 1, CK_EYE);
+ node = m_pAnimationKeys;
+ }
+ else
+ node = AddNode(node, 1, CK_EYE);
+ file->Read(&node->time, 2);
+ file->Read(node->param, 12);
+ file->Read(&node->type, 1);
+ }
+
+ file->Read(&m_fovy, 4);
+ file->Read(&m_zFar, 4);
+ file->Read(&m_zNear, 4);
+
+ if (version < 5)
+ {
+ file->Read(&n, 4);
+ if (n != 0)
+ m_nState |= LC_CAMERA_HIDDEN;
+ }
+ else
+ {
+ file->Read(&m_nState, 1);
+ file->Read(&m_nType, 1);
+ }
+ }
+
+ if ((version > 1) && (version < 4))
+ {
+ unsigned long show;
+ int user;
+
+ file->Read(&show, 4);
+// if (version > 2)
+ file->Read(&user, 4);
+ if (show == 0)
+ m_nState |= LC_CAMERA_HIDDEN;
+ }
+}
+
+void Camera::FileSave(File* file)
+{
+ int n;
+ CAMERA_KEY *node;
+ unsigned char ch = 5; // LeoCAD 0.70
+
+ file->Write(&ch, 1);
+ ch = strlen(m_strName);
+ file->Write(&ch, 1);
+ file->Write(m_strName, ch);
+
+ for (n = 0, node = m_pInstructionKeys; node; node = node->next)
+ n++;
+ file->Write(&n, 4);
+
+ for (node = m_pInstructionKeys; node; node = node->next)
+ {
+ file->Write(&node->time, 2);
+ file->Write(node->param, 12);
+ file->Write(&node->type, 1);
+ }
+
+ for (n = 0, node = m_pAnimationKeys; node; node = node->next)
+ n++;
+ file->Write(&n, 4);
+
+ for (node = m_pAnimationKeys; node; node = node->next)
+ {
+ file->Write(&node->time, 2);
+ file->Write(node->param, 12);
+ file->Write(&node->type, 1);
+ }
+
+ file->Write(&m_fovy, 4);
+ file->Write(&m_zFar, 4);
+ file->Write(&m_zNear, 4);
+ // version 5
+ file->Write(&m_nState, 1);
+ file->Write(&m_nType, 1);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Camera operations
+
+void Camera::ChangeKey(unsigned short nTime, bool bAnimation, bool bAddKey, float param[3], unsigned char nKeyType)
+{
+ CAMERA_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;
+
+ newpos->param[0] = param[0];
+ newpos->param[1] = param[1];
+ newpos->param[2] = param[2];
+}
+
+void Camera::Move(unsigned short nTime, bool bAnimation, bool bAddKey, float x, float y, float z)
+{
+ if (IsSide())
+ {
+ m_fEye[0] += x;
+ m_fEye[1] += y;
+ m_fEye[2] += z;
+ m_fTarget[0] += x;
+ m_fTarget[1] += y;
+ m_fTarget[2] += z;
+
+ ChangeKey(nTime, bAnimation, bAddKey, m_fEye, CK_EYE);
+ ChangeKey(nTime, bAnimation, bAddKey, m_fTarget, CK_TARGET);
+ }
+ else
+ {
+ if (IsEyeSelected())
+ {
+ m_fEye[0] += x;
+ m_fEye[1] += y;
+ m_fEye[2] += z;
+
+ ChangeKey(nTime, bAnimation, bAddKey, m_fEye, CK_EYE);
+ }
+
+ if (IsTargetSelected())
+ {
+ m_fTarget[0] += x;
+ m_fTarget[1] += y;
+ m_fTarget[2] += z;
+
+ ChangeKey(nTime, bAnimation, bAddKey, m_fTarget, CK_TARGET);
+ }
+
+ // Fix the up vector
+ Vector upvec(m_fUp), frontvec(m_fTarget[0]-m_fEye[0], m_fTarget[1]-m_fEye[1], m_fTarget[2]-m_fEye[2]), sidevec;
+ sidevec.Cross(frontvec, upvec);
+ upvec.Cross(sidevec, frontvec);
+ upvec.Normalize();
+ upvec.ToFloat(m_fUp);
+
+ ChangeKey(nTime, bAnimation, bAddKey, m_fUp, CK_UP);
+ }
+}
+
+void Camera::RemoveKeys()
+{
+ CAMERA_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 Camera::CalculatePosition(unsigned short nTime, bool bAnimation, float eye[3], float target[3], float up[3])
+{
+ CAMERA_KEY *node, *pe = NULL, *ne = NULL, *pt = NULL, *nt = NULL, *pu = NULL, *nu = NULL;
+ if (bAnimation)
+ node = m_pAnimationKeys;
+ else
+ node = m_pInstructionKeys;
+
+ while (node && (!ne || !nt || !nu))
+ {
+ if (node->time <= nTime)
+ {
+ switch (node->type)
+ {
+ case CK_EYE: pe = node; break;
+ case CK_TARGET: pt = node; break;
+ case CK_UP: pu = node; break;
+ }
+ }
+ else
+ {
+ switch (node->type)
+ {
+ case CK_EYE: if (ne == NULL) ne = node; break;
+ case CK_TARGET: if (nt == NULL) nt = node; break;
+ case CK_UP: if (nu == NULL) nu = node; break;
+ }
+ }
+
+ node = node->next;
+ }
+
+ // TODO: USE KEY IN/OUT WEIGHTS
+ if (bAnimation && (ne != NULL) && (pe->time != nTime))
+ {
+ float t = (float)(nTime - pe->time)/(ne->time - pe->time);
+ eye[0] = pe->param[0] + (ne->param[0] - pe->param[0])*t;
+ eye[1] = pe->param[1] + (ne->param[1] - pe->param[1])*t;
+ eye[2] = pe->param[2] + (ne->param[2] - pe->param[2])*t;
+ }
+ else
+ memcpy (eye, pe->param, sizeof(float[3]));
+
+ if (bAnimation && (nt != NULL) && (pt->time != nTime))
+ {
+ float t = (float)(nTime - pt->time)/(nt->time - pt->time);
+ target[0] = pt->param[0] + (nt->param[0] - pt->param[0])*t;
+ target[1] = pt->param[1] + (nt->param[1] - pt->param[1])*t;
+ target[2] = pt->param[2] + (nt->param[2] - pt->param[2])*t;
+ }
+ else
+ memcpy (target, pt->param, sizeof(float[3]));
+
+ if (bAnimation && (nu != NULL) && (pu->time != nTime))
+ {
+ float t = (float)(nTime - pu->time)/(nu->time - pu->time);
+ up[0] = pu->param[0] + (nu->param[0] - pu->param[0])*t;
+ up[1] = pu->param[1] + (nu->param[1] - pu->param[1])*t;
+ up[2] = pu->param[2] + (nu->param[2] - pu->param[2])*t;
+ }
+ else
+ memcpy (up, pu->param, sizeof(float[3]));
+
+ // Fix the up vector
+ Vector upvec(up), frontvec(eye[0]-target[0], eye[1]-target[1], eye[2]-target[2]), sidevec;
+ sidevec.Cross(frontvec, upvec);
+ upvec.Cross(sidevec, frontvec);
+ upvec.Normalize();
+ upvec.ToFloat(up);
+}
+
+void Camera::UpdatePosition(unsigned short nTime, bool bAnimation)
+{
+ CalculatePosition(nTime, bAnimation, m_fEye, m_fTarget, m_fUp);
+
+ float len;
+ Vector upvec(m_fUp), frontvec(m_fTarget[0]-m_fEye[0], m_fTarget[1]-m_fEye[1], m_fTarget[2]-m_fEye[2]), sidevec;
+ len = frontvec.Length();
+ sidevec.Cross(frontvec, upvec);
+ sidevec.Normalize();
+ upvec.Normalize();
+ frontvec.Normalize();
+
+ double m[16], inverse[16];
+#define M(row,col) m[col*4+row]
+ M(0,0) = sidevec.X(); M(0,1) = sidevec.Y(); M(0,2) = sidevec.Z(); M(0,3) = 0.0;
+ M(1,0) = upvec.X(); M(1,1) = upvec.Y(); M(1,2) = upvec.Z(); M(1,3) = 0.0;
+ M(2,0) = frontvec.X(); M(2,1) = frontvec.Y(); M(2,2) = frontvec.Z(); M(2,3) = 0.0;
+ M(3,0) = 0.0; M(3,1) = 0.0; M(3,2) = 0.0; M(3,3) = 1.0;
+#undef M
+ invert(m, inverse);
+
+ Matrix mat(inverse);
+ mat.SetTranslation(m_fEye[0], m_fEye[1], m_fEye[2]);
+ m_BoundingBox.CalculateBoundingBox(&mat);
+ mat.SetTranslation(m_fTarget[0], m_fTarget[1], m_fTarget[2]);
+ m_TargetBoundingBox.CalculateBoundingBox(&mat);
+
+ if (m_nList == 0)
+ m_nList = glGenLists(1);
+
+ glNewList(m_nList, GL_COMPILE);
+
+ glPushMatrix();
+ glTranslatef(m_fEye[0], m_fEye[1], m_fEye[2]);
+ glMultMatrixd(inverse);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ float verts[34][3] = {
+ { 0.3f, 0.3f, 0.3f }, { -0.3f, 0.3f, 0.3f },
+ { -0.3f, 0.3f, 0.3f }, { -0.3f, -0.3f, 0.3f },
+ { -0.3f, -0.3f, 0.3f }, { 0.3f, -0.3f, 0.3f },
+ { 0.3f, -0.3f, 0.3f }, { 0.3f, 0.3f, 0.3f },
+ { 0.3f, 0.3f, -0.3f }, { -0.3f, 0.3f, -0.3f },
+ { -0.3f, 0.3f, -0.3f }, { -0.3f, -0.3f, -0.3f },
+ { -0.3f, -0.3f, -0.3f }, { 0.3f, -0.3f, -0.3f },
+ { 0.3f, -0.3f, -0.3f }, { 0.3f, 0.3f, -0.3f },
+ { 0.3f, 0.3f, 0.3f }, { 0.3f, 0.3f, -0.3f },
+ { -0.3f, 0.3f, 0.3f }, { -0.3f, 0.3f, -0.3f },
+ { -0.3f, -0.3f, 0.3f }, { -0.3f, -0.3f, -0.3f },
+ { 0.3f, -0.3f, 0.3f }, { 0.3f, -0.3f, -0.3f },
+ { -0.3f, -0.3f, 0.6f }, { -0.3f, 0.3f, 0.6f },
+ { 0.0f, 0.0f, 0.3f }, { -0.3f, -0.3f, 0.6f },
+ { 0.3f, -0.3f, 0.6f }, { 0.0f, 0.0f, 0.3f },
+ { 0.3f, 0.3f, 0.6f }, { 0.3f, -0.3f, 0.6f },
+ { 0.3f, 0.3f, 0.6f }, { -0.3f, 0.3f, 0.6f } };
+ glVertexPointer (3, GL_FLOAT, 0, verts);
+ glDrawArrays(GL_LINES, 0, 24);
+ glDrawArrays(GL_LINE_STRIP, 24, 10);
+
+// glBegin(GL_LINES);
+// glVertex3f(0,0,0);
+// glVertex3f(0,0,len);
+// glEnd();
+
+ glTranslatef(0, 0, len);
+
+ glEndList();
+
+ if (_nTargetList == 0)
+ {
+ _nTargetList = glGenLists(1);
+ glNewList(_nTargetList, GL_COMPILE);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ float box[24][3] = {
+ { 0.2f, 0.2f, 0.2f }, { -0.2f, 0.2f, 0.2f },
+ { -0.2f, 0.2f, 0.2f }, { -0.2f, -0.2f, 0.2f },
+ { -0.2f, -0.2f, 0.2f }, { 0.2f, -0.2f, 0.2f },
+ { 0.2f, -0.2f, 0.2f }, { 0.2f, 0.2f, 0.2f },
+ { 0.2f, 0.2f, -0.2f }, { -0.2f, 0.2f, -0.2f },
+ { -0.2f, 0.2f, -0.2f }, { -0.2f, -0.2f, -0.2f },
+ { -0.2f, -0.2f, -0.2f }, { 0.2f, -0.2f, -0.2f },
+ { 0.2f, -0.2f, -0.2f }, { 0.2f, 0.2f, -0.2f },
+ { 0.2f, 0.2f, 0.2f }, { 0.2f, 0.2f, -0.2f },
+ { -0.2f, 0.2f, 0.2f }, { -0.2f, 0.2f, -0.2f },
+ { -0.2f, -0.2f, 0.2f }, { -0.2f, -0.2f, -0.2f },
+ { 0.2f, -0.2f, 0.2f }, { 0.2f, -0.2f, -0.2f } };
+ glVertexPointer (3, GL_FLOAT, 0, box);
+ glDrawArrays(GL_LINES, 0, 24);
+ glPopMatrix();
+ glEndList();
+ }
+}
+
+void Camera::Render(float fLineWidth)
+{
+ if (IsEyeSelected())
+ {
+ glLineWidth(fLineWidth*2);
+ glColor3fv (FlatColorArray[(m_nState & LC_CAMERA_FOCUSED) != 0 ? LC_COL_FOCUSED : LC_COL_SELECTED]);
+ glCallList(m_nList);
+ glLineWidth(fLineWidth);
+ }
+ else
+ {
+ glColor3f(0.5f, 0.8f, 0.5f);
+ glCallList(m_nList);
+ }
+
+ if (IsTargetSelected())
+ {
+ glLineWidth(fLineWidth*2);
+ glColor3fv (FlatColorArray[(m_nState & LC_CAMERA_TARGET_FOCUSED) != 0 ? LC_COL_FOCUSED : LC_COL_SELECTED]);
+ glCallList(_nTargetList);
+ glLineWidth(fLineWidth);
+ }
+ else
+ {
+ glColor3f(0.5f, 0.8f, 0.5f);
+ glCallList(_nTargetList);
+ }
+
+ glColor3f(0.5f, 0.8f, 0.5f);
+ glBegin(GL_LINES);
+ glVertex3fv(m_fEye);
+ glVertex3fv(m_fTarget);
+ glEnd();
+
+ if (IsSelected())
+ {
+ double projection[16], modelview[16], inverse[16];
+
+ float len;
+ Vector frontvec(m_fTarget[0]-m_fEye[0], m_fTarget[1]-m_fEye[1], m_fTarget[2]-m_fEye[2]);
+ len = frontvec.Length();
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ gluPerspective(m_fovy, 1.33f, 0.01, len);
+ glGetDoublev(GL_PROJECTION_MATRIX, projection);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+ gluLookAt(m_fEye[0], m_fEye[1], m_fEye[2], m_fTarget[0], m_fTarget[1], m_fTarget[2], m_fUp[0], m_fUp[1], m_fUp[2]);
+ glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
+ glPopMatrix();
+
+ glPushMatrix();
+ invert(modelview, inverse);
+ glMultMatrixd(inverse);
+ invert(projection, inverse);
+ glMultMatrixd(inverse);
+
+ // draw the viewing frustum
+ glBegin(GL_LINE_LOOP);
+ glVertex3i(1, 1, 1);
+ glVertex3i(-1, 1, 1);
+ glVertex3i(-1, -1, 1);
+ glVertex3i(1, -1, 1);
+ glEnd();
+
+ glBegin(GL_LINES);
+ glVertex3i(1, 1, -1);
+ glVertex3i(1, 1, 1);
+ glVertex3i(-1, 1, -1);
+ glVertex3i(-1, 1, 1);
+ glVertex3i(-1, -1, -1);
+ glVertex3i(-1, -1, 1);
+ glVertex3i(1, -1, -1);
+ glVertex3i(1, -1, 1);
+ glEnd();
+
+ glPopMatrix();
+ }
+}
+
+void Camera::MinIntersectDist(CLICKLINE* pLine)
+{
+ double dist;
+
+ if (m_nState & LC_CAMERA_HIDDEN)
+ return;
+
+ dist = m_BoundingBox.FindIntersectDist(pLine);
+
+ if (dist < pLine->mindist)
+ {
+ pLine->mindist = dist;
+ pLine->pClosest = &m_BoundingBox;
+ }
+
+ dist = m_TargetBoundingBox.FindIntersectDist(pLine);
+
+ if (dist < pLine->mindist)
+ {
+ pLine->mindist = dist;
+ pLine->pClosest = &m_TargetBoundingBox;
+ }
+}
+
+void Camera::LoadProjection(float fAspect)
+{
+ if (m_pTR != NULL)
+ m_pTR->BeginTile();
+ else
+ {
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective(m_fovy, fAspect, m_zNear, m_zFar);
+/*
+ ymax = 10;//(m_zFar-m_zNear)*tan(DTOR*m_fovy)/3;
+ ymin = -ymax;
+ xmin = ymin * fAspect;
+ xmax = ymax * fAspect;
+ znear = -60;
+ zfar = 60;
+ glOrtho(xmin, xmax, ymin, ymax, znear, zfar);
+*/
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt(m_fEye[0], m_fEye[1], m_fEye[2], m_fTarget[0], m_fTarget[1], m_fTarget[2], m_fUp[0], m_fUp[1], m_fUp[2]);
+}
+
+void Camera::DoZoom(int dy, int mouse, unsigned short nTime, bool bAnimation, bool bAddKey)
+{
+ Vector frontvec(m_fEye[0]-m_fTarget[0], m_fEye[1]-m_fTarget[1], m_fEye[2]-m_fTarget[2]);
+ frontvec.Normalize();
+ frontvec *= 2.0f*dy/(21-mouse);
+
+ // TODO: option to move eye, target or both
+ m_fEye[0] += frontvec.X();
+ m_fEye[1] += frontvec.Y();
+ m_fEye[2] += frontvec.Z();
+ m_fTarget[0] += frontvec.X();
+ m_fTarget[1] += frontvec.Y();
+ m_fTarget[2] += frontvec.Z();
+
+ ChangeKey(nTime, bAnimation, bAddKey, m_fEye, CK_EYE);
+ ChangeKey(nTime, bAnimation, bAddKey, m_fTarget, CK_TARGET);
+ UpdatePosition(nTime, bAnimation);
+}
+
+void Camera::DoPan(int dx, int dy, int mouse, unsigned short nTime, bool bAnimation, bool bAddKey)
+{
+ Vector upvec(m_fUp), frontvec(m_fEye[0]-m_fTarget[0], m_fEye[1]-m_fTarget[1], m_fEye[2]-m_fTarget[2]), sidevec;
+ sidevec.Cross(frontvec, upvec);
+ sidevec.Normalize();
+ sidevec *= 2.0f*dx/(21-mouse);
+ upvec.Normalize();
+ upvec *= -2.0f*dy/(21-mouse);
+
+ m_fEye[0] += upvec.X() + sidevec.X();
+ m_fEye[1] += upvec.Y() + sidevec.Y();
+ m_fEye[2] += upvec.Z() + sidevec.Z();
+ m_fTarget[0] += upvec.X() + sidevec.X();
+ m_fTarget[1] += upvec.Y() + sidevec.Y();
+ m_fTarget[2] += upvec.Z() + sidevec.Z();
+
+ ChangeKey(nTime, bAnimation, bAddKey, m_fEye, CK_EYE);
+ ChangeKey(nTime, bAnimation, bAddKey, m_fTarget, CK_TARGET);
+ UpdatePosition(nTime, bAnimation);
+}
+
+void Camera::DoRotate(int dx, int dy, int mouse, unsigned short nTime, bool bAnimation, bool bAddKey, float* center)
+{
+ Vector upvec(m_fUp), frontvec(m_fEye[0]-m_fTarget[0], m_fEye[1]-m_fTarget[1], m_fEye[2]-m_fTarget[2]), sidevec;
+ sidevec.Cross(frontvec, upvec);
+ sidevec.Normalize();
+ sidevec *= 2.0f*dx/(21-mouse);
+ upvec.Normalize();
+ upvec *= -2.0f*dy/(21-mouse);
+
+ // TODO: option to move eye or target
+ float len = frontvec.Length();
+ frontvec.Add(upvec.X() + sidevec.X(), upvec.Y() + sidevec.Y(), upvec.Z() + sidevec.Z());
+ frontvec.Normalize();
+ frontvec *= len;
+ frontvec.Add(m_fTarget);
+ frontvec.ToFloat(m_fEye);
+
+ // Calculate new up
+ upvec.FromFloat(m_fUp);
+ frontvec.FromFloat(m_fEye[0]-m_fTarget[0], m_fEye[1]-m_fTarget[1], m_fEye[2]-m_fTarget[2]);
+ sidevec.Cross(frontvec, upvec);
+ upvec.Cross(sidevec, frontvec);
+ upvec.Normalize();
+ upvec.ToFloat(m_fUp);
+
+ ChangeKey(nTime, bAnimation, bAddKey, m_fEye, CK_EYE);
+ ChangeKey(nTime, bAnimation, bAddKey, m_fUp, CK_UP);
+ UpdatePosition(nTime, bAnimation);
+}
+
+void Camera::DoRoll(int dx, int mouse, unsigned short nTime, bool bAnimation, bool bAddKey)
+{
+ Matrix mat;
+ float front[3] = { m_fEye[0]-m_fTarget[0], m_fEye[1]-m_fTarget[1], m_fEye[2]-m_fTarget[2] };
+
+ mat.FromAxisAngle(front, 2.0f*dx/(21-mouse));
+ mat.TransformPoints(m_fUp, 1);
+
+ ChangeKey(nTime, bAnimation, bAddKey, m_fUp, CK_UP);
+ UpdatePosition(nTime, bAnimation);
+}
+
+void Camera::StartTiledRendering(int tw, int th, int iw, int ih, float fAspect)
+{
+ m_pTR = new TiledRender();
+ m_pTR->TileSize(tw, th, 0);
+ m_pTR->ImageSize(iw, ih);
+ m_pTR->Perspective(m_fovy, fAspect, m_zNear, m_zFar);
+}
+
+void Camera::GetTileInfo(int* row, int* col, int* width, int* height)
+{
+ if (m_pTR != NULL)
+ {
+ *row = m_pTR->m_Rows - m_pTR->m_CurrentRow - 1;
+ *col = m_pTR->m_CurrentColumn;
+ *width = m_pTR->m_CurrentTileWidth;
+ *height = m_pTR->m_CurrentTileHeight;
+ }
+}
+
+bool Camera::EndTile()
+{
+ if (m_pTR != NULL)
+ {
+ if (m_pTR->EndTile())
+ return true;
+
+ delete m_pTR;
+ m_pTR = NULL;
+ }
+
+ return false;
+}
diff --git a/common/camera.h b/common/camera.h
new file mode 100644
index 0000000..cb5a5cb
--- /dev/null
+++ b/common/camera.h
@@ -0,0 +1,135 @@
+//
+// camera.h
+////////////////////////////////////////////////////
+
+#ifndef _CAMERA_H_
+#define _CAMERA_H_
+
+#ifndef GLuint
+#include "GL/gl.h"
+#endif
+#include "boundbox.h"
+
+#define LC_CAMERA_HIDDEN 0x01
+#define LC_CAMERA_SELECTED 0x02
+#define LC_CAMERA_FOCUSED 0x04
+#define LC_CAMERA_TARGET_SELECTED 0x08
+#define LC_CAMERA_TARGET_FOCUSED 0x10
+
+typedef enum { LC_CAMERA_FRONT,LC_CAMERA_BACK,
+ LC_CAMERA_TOP, LC_CAMERA_UNDER,
+ LC_CAMERA_LEFT, LC_CAMERA_RIGHT,
+ LC_CAMERA_MAIN, LC_CAMERA_USER } LC_CAMERA_TYPES;
+
+typedef enum { CK_EYE, CK_TARGET, CK_UP } CK_TYPES;
+
+typedef struct CAMERA_KEY {
+ unsigned short time;
+ float param[3];
+ unsigned char type;
+ CAMERA_KEY* next;
+} CAMERA_KEY;
+
+class File;
+class TiledRender;
+
+class Camera
+{
+public:
+ Camera();
+ Camera(int nType, Camera* pPrev);
+ Camera(float ex, float ey, float ez, float tx, float ty, float tz, Camera* pCamera);
+ Camera(float eye[3], float target[3], float up[3], Camera* pCamera);
+ ~Camera();
+
+ Camera* m_pNext;
+ void Hide()
+ { m_nState = LC_CAMERA_HIDDEN; }
+ void UnHide()
+ { m_nState &= ~LC_CAMERA_HIDDEN; }
+ char* GetName()
+ { return m_strName; }
+ bool IsSide()
+ { return m_nType < LC_CAMERA_MAIN; }
+ bool IsUser()
+ { return m_nType == LC_CAMERA_USER; }
+ bool IsVisible()
+ { return (m_nState & LC_CAMERA_HIDDEN) == 0; }
+ bool IsSelected()
+ { return (m_nState & (LC_CAMERA_SELECTED|LC_CAMERA_TARGET_SELECTED)) != 0; }
+ bool IsEyeSelected()
+ { return (m_nState & LC_CAMERA_SELECTED) != 0; }
+ bool IsTargetSelected()
+ { return (m_nState & LC_CAMERA_TARGET_SELECTED) != 0; }
+ void Select()
+ { m_nState |= (LC_CAMERA_SELECTED|LC_CAMERA_TARGET_SELECTED); }
+ void UnSelect()
+ { m_nState &= ~(LC_CAMERA_SELECTED|LC_CAMERA_FOCUSED|LC_CAMERA_TARGET_SELECTED|LC_CAMERA_TARGET_FOCUSED); }
+ void UnFocus()
+ { m_nState &= ~(LC_CAMERA_FOCUSED|LC_CAMERA_TARGET_FOCUSED); }
+ bool IsEyeFocused()
+ { return (m_nState & LC_CAMERA_FOCUSED) != 0; }
+ void FocusEye()
+ { m_nState |= (LC_CAMERA_FOCUSED|LC_CAMERA_SELECTED); }
+ bool IsTargetFocused()
+ { return (m_nState & LC_CAMERA_TARGET_FOCUSED) != 0; }
+ void FocusTarget()
+ { m_nState |= (LC_CAMERA_TARGET_FOCUSED|LC_CAMERA_TARGET_SELECTED); }
+
+ void GetEye(float* eye)
+ { memcpy(eye, m_fEye, sizeof(m_fEye)); };
+ void GetTarget(float* target)
+ { memcpy(target, m_fTarget, sizeof(m_fTarget)); };
+ void GetUp(float* up)
+ { memcpy(up, m_fUp, sizeof(m_fUp)); };
+
+public:
+ void MinIntersectDist(CLICKLINE* Line);
+ void ChangeKey(unsigned short nTime, bool bAnimation, bool bAddKey, float param[3], unsigned char nKeyType);
+ void UpdatePosition(unsigned short nTime, bool bAnimation);
+ void Render(float fLineWidth);
+ void LoadProjection(float fAspect);
+ void FileLoad(File* file);
+ void FileSave(File* file);
+
+ void DoZoom(int dy, int mouse, unsigned short nTime, bool bAnimation, bool bAddKey);
+ void DoPan(int dx, int dy, int mouse, unsigned short nTime, bool bAnimation, bool bAddKey);
+ void DoRotate(int dx, int dy, int mouse, unsigned short nTime, bool bAnimation, bool bAddKey, float* center);
+ void DoRoll(int dx, int mouse, unsigned short nTime, bool bAnimation, bool bAddKey);
+ void Move(unsigned short nTime, bool bAnimation, bool bAddKey, float x, float y, float z);
+
+ void StartTiledRendering(int tw, int th, int iw, int ih, float fAspect);
+ void GetTileInfo(int* row, int* col, int* width, int* height);
+ bool EndTile();
+
+ float m_fovy;
+ float m_zNear;
+ float m_zFar;
+
+protected:
+ void CalculatePosition(unsigned short nTime, bool bAnimation, float eye[3], float target[3], float up[3]);
+ void RemoveKeys();
+ void Initialize();
+
+ // For using the mouse
+ BoundingBox m_BoundingBox;
+ BoundingBox m_TargetBoundingBox;
+
+ // Position
+ CAMERA_KEY* m_pAnimationKeys;
+ CAMERA_KEY* m_pInstructionKeys;
+
+ // Attributes
+ char m_strName[81];
+ unsigned char m_nState;
+ unsigned char m_nType;
+ GLuint m_nList;
+
+ // Temporary position
+ float m_fEye[3];
+ float m_fTarget[3];
+ float m_fUp[3];
+ TiledRender* m_pTR;
+};
+
+#endif // _CAMERA_H_
diff --git a/common/defines.h b/common/defines.h
new file mode 100644
index 0000000..25366a8
--- /dev/null
+++ b/common/defines.h
@@ -0,0 +1,180 @@
+// Constant definitions.
+//
+
+#ifndef _DEFINES_H_
+#define _DEFINES_H_
+
+/////////////////////////////////////////////////////////////////////////////
+// System specific
+
+#if ! ( defined( _WINDOWS ) || defined( _LINUX ) )
+#error YOU NEED TO DEFINE YOUR OS
+#endif
+
+#ifdef _WINDOWS
+#define LC_MAXPATH _MAX_PATH
+#define KEY_SHIFT VK_SHIFT
+#define KEY_CONTROL VK_CONTROL
+#define KEY_ESCAPE VK_ESCAPE
+#define KEY_TAB VK_TAB
+#define KEY_INSERT VK_INSERT
+#define KEY_DELETE VK_DELETE
+#define KEY_UP VK_UP
+#define KEY_DOWN VK_DOWN
+#define KEY_LEFT VK_LEFT
+#define KEY_RIGHT VK_RIGHT
+#define KEY_PRIOR VK_PRIOR
+#define KEY_NEXT VK_NEXT
+#define KEY_PLUS VK_ADD
+#define KEY_MINUS VK_SUBTRACT
+#endif
+
+#ifdef _LINUX
+#define LC_MAXPATH 1024 //FILENAME_MAX
+#define KEY_SHIFT 0x01
+#define KEY_CONTROL 0x02
+#define KEY_ESCAPE 0x03
+#define KEY_TAB 0x04
+#define KEY_INSERT 0x05
+#define KEY_DELETE 0x06
+#define KEY_UP 0x07
+#define KEY_DOWN 0x08
+#define KEY_LEFT 0x09
+#define KEY_RIGHT 0x0A
+#define KEY_PRIOR 0x0B
+#define KEY_NEXT 0x0C
+#define KEY_PLUS '+'
+#define KEY_MINUS '-'
+
+char* strupr(char* string);
+char* strlwr(char* string);
+
+#endif
+
+// Endianess handling (files are little endian).
+#ifdef _BIG_ENDIAN
+unsigned short SwapSHORT(unsigned short x)
+{ return (x>>8) | (x<<8); }
+unsigned long SwapLONG(unsigned long x)
+{ return (x>>24) | ((x>>8) & 0xff00) | ((x<<8) & 0xff0000) | (x<<24); }
+#define SHORT(x) ((short)SwapSHORT((unsigned short) (x)))
+#define LONG(x) ((long)SwapLONG((unsigned long) (x)))
+#else
+#define SHORT(x) (x)
+#define LONG(x) (x)
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// LeoCAD constants
+
+// Math numbers
+#define DTOR 0.017453f
+#define RTOD 57.29578f
+#define PI 3.14159265
+#define PI2 6.28318530
+
+#ifndef min
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef max
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef ABS
+#define ABS(a) (((a) > 0) ? (a) : -(a))
+#endif
+
+#ifndef RGB
+#define RGB(r, g, b) ((unsigned long)(((unsigned char) (r) | ((unsigned short) (g) << 8))|(((unsigned long) (unsigned char) (b)) << 16)))
+#endif
+
+#define FLOATRGB(f) RGB(f[0]*255, f[1]*255, f[2]*255)
+
+#define LC_CONNECTIONS 2 // Different piece connections
+#define LC_APP_VERSION 0.71f // Current version
+#define LC_STR_VERSION "LeoCAD 0.7 Project\0\0" // char[20]
+
+#define LC_MAXCOLORS 28 // Number of colors supported
+#define LC_COL_EDGES 28 // Piece edges
+#define LC_COL_SELECTED 29 // Selected object
+#define LC_COL_FOCUSED 30 // Focused object
+#define LC_COL_DEFAULT 31 // Default piece color
+
+
+// #define DET_BACKFACES 0x00001 // Draw backfaces
+// #define DET_DEPTH 0x00002 // Enable depth test
+// #define DET_CLEAR 0x00004 // Use clear colors
+#define LC_DET_LIGHTING 0x00008 // Lighting
+#define LC_DET_SMOOTH 0x00010 // Smooth shading
+// #define DET_STUDS 0x00020 // Draw studs
+// #define DET_WIREFRAME 0x00040 // Wireframe
+#define LC_DET_ANTIALIAS 0x00080 // Turn on anti-aliasing
+#define LC_DET_BRICKEDGES 0x00100 // Draw lines
+#define LC_DET_DITHER 0x00200 // Enable dithering
+#define LC_DET_BOX_FILL 0x00400 // Filled boxes
+#define LC_DET_HIDDEN_LINE 0x00800 // Remove hidden lines
+// #define DET_STUDS_BOX 0x01000 // Draw studs as boxes
+#define LC_DET_LINEAR 0x02000 // Linear filtering
+#define LC_DET_FAST 0x04000 // Fast rendering (boxes)
+#define LC_DET_BACKGROUND 0x08000 // Background rendering
+#define LC_DET_SCREENDOOR 0x10000 // No alpha blending
+
+#define LC_DRAW_AXIS 0x0001 // Orientation icon
+#define LC_DRAW_GRID 0x0002 // Grid
+#define LC_DRAW_SNAP_A 0x0004 // Snap Angle
+#define LC_DRAW_SNAP_X 0x0008 // Snap X
+#define LC_DRAW_SNAP_Y 0x0010 // Snap Y
+#define LC_DRAW_SNAP_Z 0x0020 // Snap Z
+//#define DRAW_COLLISION 0x0040
+#define LC_DRAW_MOVE 0x0080 // Switch to move after insert
+#define LC_DRAW_LOCK_X 0x0100 // Lock X
+#define LC_DRAW_LOCK_Y 0x0200 // Lock Y
+#define LC_DRAW_LOCK_Z 0x0400 // Lock Z
+#define LC_DRAW_MOVEAXIS 0x0800 // Move on fixed axis
+#define LC_DRAW_PREVIEW 0x1000 // Show piece position
+#define LC_DRAW_CM_UNITS 0x2000 // Use centimeters
+#define LC_DRAW_3DMOUSE 0x4000 // Mouse moves in all directions
+
+// #define RENDER_FAST 0x001
+// #define RENDER_BACKGROUND 0x002
+#define LC_SCENE_FOG 0x004 // Enable fog
+// #define RENDER_FOG_BG 0x008 // Use bg color for fog
+#define LC_SCENE_BG 0x010 // Draw bg image
+// #define RENDER_BG_FAST 0x020
+#define LC_SCENE_BG_TILE 0x040 // Tile bg image
+#define LC_SCENE_FLOOR 0x080 // Render floor
+#define LC_SCENE_GRADIENT 0x100 // Draw gradient
+
+#define LC_TERRAIN_FLAT 0x01 // Flat terrain
+#define LC_TERRAIN_TEXTURE 0x02 // Use texture
+#define LC_TERRAIN_SMOOTH 0x04 // Smooth shading
+
+#define LC_AUTOSAVE_FLAG 0x100000 // Enable auto-saving
+
+#define LC_SEL_NO_PIECES 0x001 // No pieces in the project
+#define LC_SEL_PIECE 0x002 // piece selected
+#define LC_SEL_CAMERA 0x004 // camera selected
+#define LC_SEL_LIGHT 0x010 // light selected
+#define LC_SEL_MULTIPLE 0x020 // multiple pieces selected
+#define LC_SEL_UNSELECTED 0x040 // at least 1 piece unselected
+#define LC_SEL_HIDDEN 0x080 // at least one piece hidden
+#define LC_SEL_GROUP 0x100 // at least one piece selected is grouped
+#define LC_SEL_FOCUSGROUP 0x200 // focused piece is grouped
+#define LC_SEL_CANGROUP 0x400 // can make a new group
+
+// Image Options
+#define LC_IMAGE_PROGRESSIVE 0x1000
+#define LC_IMAGE_TRANSPARENT 0x2000
+#define LC_IMAGE_HIGHCOLOR 0x4000
+#define LC_IMAGE_MASK 0x7000
+
+// Bounding box owner
+typedef enum { LC_PIECE, LC_CAMERA, LC_CAMERA_TARGET,
+ LC_LIGHT, LC_LIGHT_TARGET } LC_OBJ_TYPES;
+
+#define LC_UPDATE_OBJECT 0x40
+#define LC_UPDATE_TYPE 0x80
+
+
+#endif // _DEFINES_H_
diff --git a/common/file.cpp b/common/file.cpp
new file mode 100644
index 0000000..2791f7a
--- /dev/null
+++ b/common/file.cpp
@@ -0,0 +1,337 @@
+// File class, can be a memory file or in the disk.
+// Needed to work with the clipboard and undo/redo easily.
+
+#include <stdio.h>
+#include <memory.h>
+#include <stdlib.h>
+#include "file.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// File construction/destruction
+
+File::File(bool bMemFile)
+{
+ m_bMemFile = bMemFile;
+
+ if (m_bMemFile)
+ {
+ m_nGrowBytes = 1024;
+ m_nPosition = 0;
+ m_nBufferSize = 0;
+ m_nFileSize = 0;
+ m_pBuffer = NULL;
+ m_bAutoDelete = true;
+ }
+ else
+ {
+ m_hFile = NULL;
+ m_bCloseOnDelete = false;
+ }
+}
+
+File::~File()
+{
+ if (m_bMemFile)
+ {
+ if (m_pBuffer)
+ Close();
+
+ m_nGrowBytes = 0;
+ m_nPosition = 0;
+ m_nBufferSize = 0;
+ m_nFileSize = 0;
+ }
+ else
+ {
+ if (m_hFile != NULL && m_bCloseOnDelete)
+ Close();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// File operations
+
+char* File::ReadString(char* pBuf, unsigned long nMax)
+{
+ if (m_bMemFile)
+ {
+ int nRead = 0;
+ unsigned char ch;
+
+ if (nMax <= 0)
+ return NULL;
+ if (m_nPosition >= m_nFileSize)
+ return NULL;
+
+ while (--nMax)
+ {
+ if (m_nPosition == m_nFileSize)
+ break;
+
+ ch = m_pBuffer[m_nPosition];
+ m_nPosition++;
+ pBuf[nRead++] = ch;
+
+ if (ch == '\n')
+ break;
+ }
+
+ pBuf[nRead] = '\0';
+ return pBuf;
+ }
+ else
+ return fgets(pBuf, nMax, m_hFile);
+}
+
+unsigned long File::Read(void* pBuf, unsigned long nCount)
+{
+ if (nCount == 0)
+ return 0;
+
+ if (m_bMemFile)
+ {
+ if (m_nPosition > m_nFileSize)
+ return 0;
+
+ unsigned long nRead;
+ if (m_nPosition + nCount > m_nFileSize)
+ nRead = (unsigned long)(m_nFileSize - m_nPosition);
+ else
+ nRead = nCount;
+
+ memcpy((unsigned char*)pBuf, (unsigned char*)m_pBuffer + m_nPosition, nRead);
+ m_nPosition += nRead;
+
+ return nRead;
+ }
+ else
+ return fread(pBuf, 1, nCount, m_hFile);
+}
+
+int File::GetChar()
+{
+ if (m_bMemFile)
+ {
+ if (m_nPosition > m_nFileSize)
+ return 0;
+
+ unsigned char* ret = (unsigned char*)m_pBuffer + m_nPosition;
+ m_nPosition++;
+
+ return *ret;
+ }
+ else
+ return fgetc(m_hFile);
+}
+
+unsigned long File::Write(const void* pBuf, unsigned long nCount)
+{
+ if (nCount == 0)
+ return 0;
+
+ if (m_bMemFile)
+ {
+ if (m_nPosition + nCount > m_nBufferSize)
+ GrowFile(m_nPosition + nCount);
+
+ memcpy((unsigned char*)m_pBuffer + m_nPosition, (unsigned char*)pBuf, nCount);
+
+ m_nPosition += nCount;
+
+ if (m_nPosition > m_nFileSize)
+ m_nFileSize = m_nPosition;
+
+ return nCount;
+ }
+ else
+ return fwrite(pBuf, 1, nCount, m_hFile);
+}
+
+int File::PutChar(int c)
+{
+ if (m_bMemFile)
+ {
+ if (m_nPosition + 1 > m_nBufferSize)
+ GrowFile(m_nPosition + 1);
+
+ unsigned char* bt = (unsigned char*)m_pBuffer + m_nPosition;
+ *bt = c;
+
+ m_nPosition++;
+
+ if (m_nPosition > m_nFileSize)
+ m_nFileSize = m_nPosition;
+
+ return 1;
+ }
+ else
+ return fputc(c, m_hFile);
+}
+
+bool File::Open(const char *filename, const char *mode)
+{
+ if (m_bMemFile)
+ return false;
+ else
+ {
+ m_hFile = fopen(filename, mode);
+ m_bCloseOnDelete = true;
+
+ return (m_hFile != NULL);
+ }
+}
+
+void File::Close()
+{
+ if (m_bMemFile)
+ {
+ m_nGrowBytes = 0;
+ m_nPosition = 0;
+ m_nBufferSize = 0;
+ m_nFileSize = 0;
+ if (m_pBuffer && m_bAutoDelete)
+ free(m_pBuffer);
+ m_pBuffer = NULL;
+ }
+ else
+ {
+ if (m_hFile != NULL)
+ fclose(m_hFile);
+
+ m_hFile = NULL;
+ m_bCloseOnDelete = false;
+// m_strFileName.Empty();
+ }
+}
+
+unsigned long File::Seek(long lOff, int nFrom)
+{
+ if (m_bMemFile)
+ {
+ unsigned long lNewPos = m_nPosition;
+
+ if (nFrom == SEEK_SET)
+ lNewPos = lOff;
+ else if (nFrom == SEEK_CUR)
+ lNewPos += lOff;
+ else if (nFrom == SEEK_END)
+ lNewPos = m_nFileSize + lOff;
+ else
+ return (unsigned long)-1;
+
+ m_nPosition = lNewPos;
+
+ return m_nPosition;
+ }
+ else
+ {
+ fseek (m_hFile, lOff, nFrom);
+
+ return ftell(m_hFile);
+ }
+}
+
+unsigned long File::GetPosition() const
+{
+ if (m_bMemFile)
+ return m_nPosition;
+ else
+ return ftell(m_hFile);
+}
+
+void File::GrowFile(unsigned long nNewLen)
+{
+ if (m_bMemFile)
+ {
+ if (nNewLen > m_nBufferSize)
+ {
+ // grow the buffer
+ unsigned long nNewBufferSize = m_nBufferSize;
+
+ // determine new buffer size
+ while (nNewBufferSize < nNewLen)
+ nNewBufferSize += m_nGrowBytes;
+
+ // allocate new buffer
+ unsigned char* lpNew;
+ if (m_pBuffer == NULL)
+ lpNew = (unsigned char*)malloc(nNewBufferSize);
+ else
+ lpNew = (unsigned char*)realloc(m_pBuffer, nNewBufferSize);
+
+ m_pBuffer = lpNew;
+ m_nBufferSize = nNewBufferSize;
+ }
+ }
+}
+
+void File::Flush()
+{
+ if (m_bMemFile)
+ {
+
+ }
+ else
+ {
+ if (m_hFile == NULL)
+ return;
+
+ fflush(m_hFile);
+ }
+}
+
+void File::Abort()
+{
+ if (m_bMemFile)
+ Close();
+ else
+ {
+ if (m_hFile != NULL)
+ {
+ // close but ignore errors
+ if (m_bCloseOnDelete)
+ fclose(m_hFile);
+ m_hFile = NULL;
+ m_bCloseOnDelete = false;
+ }
+// m_strFileName.Empty();
+ }
+}
+
+void File::SetLength(unsigned long nNewLen)
+{
+ if (m_bMemFile)
+ {
+ if (nNewLen > m_nBufferSize)
+ GrowFile(nNewLen);
+
+ if (nNewLen < m_nPosition)
+ m_nPosition = nNewLen;
+
+ m_nFileSize = nNewLen;
+ }
+ else
+ {
+ fseek(m_hFile, nNewLen, SEEK_SET);
+ }
+}
+
+unsigned long File::GetLength() const
+{
+ if (m_bMemFile)
+ {
+ return m_nBufferSize;
+ }
+ else
+ {
+ unsigned long nLen, nCur;
+
+ // Seek is a non const operation
+ nCur = ftell(m_hFile);
+ fseek(m_hFile, 0, SEEK_END);
+ nLen = ftell(m_hFile);
+ fseek(m_hFile, nCur, SEEK_SET);
+
+ return nLen;
+ }
+}
diff --git a/common/file.h b/common/file.h
new file mode 100644
index 0000000..8608092
--- /dev/null
+++ b/common/file.h
@@ -0,0 +1,63 @@
+//
+// file.h
+////////////////////////////////////////////////////
+
+#ifndef _FILE_H_
+#define _FILE_H_
+
+#include <stdio.h>
+
+class File
+{
+public:
+// Constructors
+ File(bool bMemFile);
+ ~File();
+
+// Implementation
+protected:
+ bool m_bMemFile;
+
+ // MemFile specific:
+ unsigned long m_nGrowBytes;
+ unsigned long m_nPosition;
+ unsigned long m_nBufferSize;
+ unsigned long m_nFileSize;
+ unsigned char* m_pBuffer;
+ bool m_bAutoDelete;
+ void GrowFile(unsigned long nNewLen);
+
+ // DiscFile specific:
+ FILE* m_hFile;
+ bool m_bCloseOnDelete;
+
+public:
+ unsigned long GetPosition() const;
+ unsigned long Seek(long lOff, int nFrom);
+ void SetLength(unsigned long nNewLen);
+ unsigned long GetLength() const;
+
+ char* ReadString(char* pBuf, unsigned long nMax);
+ unsigned long Read(void* pBuf, unsigned long nCount);
+ unsigned long Write(const void* pBuf, unsigned long nCount);
+ int GetChar();
+ int PutChar(int c);
+
+ void Abort();
+ void Flush();
+ void Close();
+ bool Open(const char *filename, const char *mode);
+
+public:
+// Attributes
+// CString GetFileName() const;
+// CString GetFileTitle() const;
+// CString GetFilePath() const;
+// void SetFilePath(LPCTSTR lpszNewName);
+
+protected:
+// CString m_strFileName;
+};
+
+
+#endif // _FILE_H_
diff --git a/common/globals.cpp b/common/globals.cpp
new file mode 100644
index 0000000..9f6af1b
--- /dev/null
+++ b/common/globals.cpp
@@ -0,0 +1,87 @@
+// Global variables common to all platforms.
+//
+
+#include "defines.h"
+
+class Project;
+Project* project;
+
+const char* colornames[LC_MAXCOLORS] = { "Red", "Orange", "Green",
+ "Light Green", "Blue", "Light Blue", "Yellow", "White",
+ "Dark Gray", "Black", "Brown", "Pink", "Purple", "Gold",
+ "Clear Red", "Clear Orange", "Clear Green", "Clear Light Green",
+ "Clear Blue", "Clear Light Blue", "Clear Yellow", "Clear White",
+ "Light Gray", "Tan", "Light Brown", "Light Pink", "Turquoise", "Silver" };
+
+const char* altcolornames[LC_MAXCOLORS] = { "Red", "Orange", "Green",
+ "LightGreen", "Blue", "LightBlue", "Yellow", "White",
+ "DarkGray", "Black", "Brown", "Pink", "Purple", "Gold",
+ "ClearRed", "ClearOrange", "ClearGreen", "ClearLightGreen",
+ "ClearBlue", "ClearLightBlue", "ClearYellow", "ClearWhite",
+ "LightGray", "Tan", "LightBrown", "LightPink", "Turquoise", "Silver" };
+
+float FlatColorArray[31][3] = {
+ { 0.65f,0.1f, 0.1f }, // 0 - Red
+ { 1.0f, 0.5f, 0.2f }, // 1 - Orange
+ { 0.1f, 0.4f, 0.1f }, // 2 - Green
+ { 0.3f, 0.6f, 0.3f }, // 3 - Light Green
+ { 0.0f, 0.2f, 0.7f }, // 4 - Blue
+ { 0.2f, 0.4f, 0.9f }, // 5 - Light Blue
+ { 0.8f, 0.8f, 0.0f }, // 6 - Yellow
+ { 0.95f,0.95f,0.95f}, // 7 - White
+ { 0.33f,0.3f, 0.3f }, // 8 - Dark Gray
+ { 0.2f, 0.2f, 0.2f }, // 9 - Black
+ { 0.4f, 0.2f, 0.2f }, //10 - Brown
+ { 0.7f, 0.3f, 0.6f }, //11 - Pink
+ { 0.6f, 0.2f, 0.6f }, //12 - Purple
+ { 0.9f, 0.7f, 0.2f }, //13 - Gold
+ { 0.6f, 0.1f, 0.1f }, //14 - Clear Red
+ { 1.0f, 0.6f, 0.3f }, //15 - Clear Orange
+ { 0.1f, 0.4f, 0.1f }, //16 - Clear Green
+ { 0.6f, 0.7f, 0.3f }, //17 - Clear Light Green
+ { 0.0f, 0.0f, 0.5f }, //18 - Clear Blue
+ { 0.2f, 0.4f, 0.9f }, //19 - Clear Light Blue
+ { 0.9f, 0.9f, 0.0f }, //20 - Clear Yellow
+ { 0.9f, 0.9f, 0.9f }, //21 - Clear White
+ { 0.55f,0.55f,0.55f}, //22 - Light Gray
+ { 0.8f, 0.8f, 0.7f }, //23 - Tan
+ { 0.6f, 0.4f, 0.4f }, //24 - Light Brown
+ { 0.9f, 0.7f, 0.9f }, //25 - Light Pink
+ { 0.1f, 0.7f, 0.8f }, //26 - Turquoise
+ { 0.8f, 0.8f, 0.8f }, //27 - Silver
+ { 0.0f, 0.0f, 0.0f }, //28 - Edges
+ { 0.9f, 0.3f, 0.4f }, //29 - Selected
+ { 0.4f, 0.3f, 0.9f }}; //30 - Focused
+
+float ColorArray[31][4] = {
+ { 0.65f,0.1f, 0.1f, 1.0f }, // 0 - Red
+ { 1.0f, 0.5f, 0.2f, 1.0f }, // 1 - Orange
+ { 0.1f, 0.4f, 0.1f, 1.0f }, // 2 - Green
+ { 0.3f, 0.6f, 0.3f, 1.0f }, // 3 - Light Green
+ { 0.0f, 0.2f, 0.7f, 1.0f }, // 4 - Blue
+ { 0.2f, 0.4f, 0.9f, 1.0f }, // 5 - Light Blue
+ { 0.8f, 0.8f, 0.0f, 1.0f }, // 6 - Yellow
+ { 0.95f,0.95f,0.95f,1.0f }, // 7 - White
+ { 0.3f, 0.3f, 0.3f, 1.0f }, // 8 - Dark Gray
+ { 0.1f, 0.1f, 0.1f, 1.0f }, // 9 - Black
+ { 0.4f, 0.2f, 0.2f, 1.0f }, //10 - Brown
+ { 0.7f, 0.3f, 0.6f, 1.0f }, //11 - Pink
+ { 0.6f, 0.2f, 0.6f, 1.0f }, //12 - Purple
+ { 0.9f, 0.7f, 0.2f, 1.0f }, //13 - Gold
+ { 0.6f, 0.1f, 0.1f, 0.6f }, //14 - Clear Red
+ { 1.0f, 0.6f, 0.3f, 0.6f }, //15 - Clear Orange
+ { 0.1f, 0.4f, 0.1f, 0.6f }, //16 - Clear Green
+ { 0.6f, 0.7f, 0.3f, 0.6f }, //17 - Clear Light Green
+ { 0.0f, 0.0f, 0.5f, 0.6f }, //18 - Clear Blue
+ { 0.2f, 0.4f, 0.9f, 0.6f }, //19 - Clear Light Blue
+ { 0.9f, 0.9f, 0.0f, 0.6f }, //20 - Clear Yellow
+ { 0.9f, 0.9f, 0.9f, 0.6f }, //21 - Clear White
+ { 0.5f, 0.5f, 0.5f, 1.0f }, //22 - Light Gray
+ { 0.8f, 0.8f, 0.7f, 1.0f }, //23 - Tan
+ { 0.6f, 0.4f, 0.4f, 1.0f }, //24 - Light Brown
+ { 0.9f, 0.7f, 0.9f, 1.0f }, //25 - Light Pink
+ { 0.1f, 0.7f, 0.8f, 1.0f }, //26 - Turquoise
+ { 0.8f, 0.8f, 0.8f, 1.0f }, //27 - Silver
+ { 0.2f, 0.2f, 0.2f, 1.0f }, //28 - Edges
+ { 0.9f, 0.3f, 0.4f, 1.0f }, //29 - Selected
+ { 0.4f, 0.3f, 0.9f, 1.0f }}; //30 - Focused
diff --git a/common/globals.h b/common/globals.h
new file mode 100644
index 0000000..39154cd
--- /dev/null
+++ b/common/globals.h
@@ -0,0 +1,12 @@
+// Global variables.
+//
+
+class Project;
+extern Project* project;
+
+#include "defines.h"
+
+extern float FlatColorArray[31][3];
+extern float ColorArray[31][4];
+extern const char* colornames[LC_MAXCOLORS];
+extern const char* altcolornames[LC_MAXCOLORS];
diff --git a/common/group.cpp b/common/group.cpp
new file mode 100644
index 0000000..e20a1f5
--- /dev/null
+++ b/common/group.cpp
@@ -0,0 +1,79 @@
+// Piece group
+//
+
+#include <stdlib.h>
+#include "group.h"
+#include "file.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// Group construction/destruction
+
+Group::Group()
+{
+ m_pGroup = NULL;
+ m_pNext = NULL;
+}
+
+Group::~Group()
+{
+
+}
+
+Group* Group::GetTopGroup()
+{
+ return m_pGroup ? m_pGroup->GetTopGroup() : this;
+}
+
+void Group::SetGroup(Group* pGroup)
+{
+ if (pGroup == this)
+ return;
+
+ if (m_pGroup != NULL && m_pGroup != (Group*)-1)
+ m_pGroup->SetGroup(pGroup);
+ else
+ m_pGroup = pGroup;
+}
+
+void Group::UnGroup(Group* pGroup)
+{
+ if (m_pGroup == pGroup)
+ m_pGroup = NULL;
+ else
+ if (m_pGroup != NULL)
+ m_pGroup->UnGroup(pGroup);
+}
+
+void Group::FileLoad(File* file)
+{
+ unsigned char version;
+ int i;
+
+ file->Read(&version, 1);
+ file->Read(m_strName, 65);
+ file->Read(m_fCenter, 12);
+ file->Read(&i, 4);
+ m_pGroup = (Group*)i;
+}
+
+void Group::FileSave(File* file, Group* pGroups)
+{
+ unsigned char version = 1; // LeoCAD 0.60
+
+ file->Write(&version, 1);
+ file->Write(m_strName, 65);
+ file->Write(m_fCenter, 12);
+
+ int i = 0;
+ if (m_pGroup == NULL)
+ i = -1;
+ else
+ {
+ for (; pGroups; pGroups = pGroups->m_pNext)
+ if (pGroups == m_pGroup)
+ break;
+ else
+ i++;
+ }
+ file->Write(&i, 4);
+}
diff --git a/common/group.h b/common/group.h
new file mode 100644
index 0000000..45418e0
--- /dev/null
+++ b/common/group.h
@@ -0,0 +1,31 @@
+//
+// group.h
+////////////////////////////////////////////////////
+
+#ifndef _GROUP_H
+#define _GROUP_H
+
+class File;
+
+class Group
+{
+public:
+// void DoSaveLoad(CArchive& ar, CCADDoc* pDoc);
+ Group();
+ ~Group();
+
+ void SetGroup(Group* pGroup);
+ void UnGroup(Group* pGroup);
+ Group* GetTopGroup();
+
+ Group* m_pNext;
+ Group* m_pGroup;
+
+ void FileLoad(File* file);
+ void FileSave(File* file, Group* pGroups);
+
+ char m_strName[65];
+ float m_fCenter[3];
+};
+
+#endif // _GROUP_H
diff --git a/common/image.cpp b/common/image.cpp
new file mode 100644
index 0000000..456c098
--- /dev/null
+++ b/common/image.cpp
@@ -0,0 +1,1608 @@
+// Image I/O routines
+//
+
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "image.h"
+#include "quant.h"
+#include "file.h"
+extern "C" {
+#include <jpeglib.h>
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// static declarations
+
+static LC_IMAGE* OpenJPG(char* filename);
+static LC_IMAGE* OpenBMP(char* filename);
+static LC_IMAGE* OpenGIF(File* file);
+static bool SaveJPG(char* filename, LC_IMAGE* image, int quality, bool progressive);
+static bool SaveBMP(char* filename, LC_IMAGE* image, bool quantize);
+static bool SaveGIF(File* file, LC_IMAGE* image, bool transparent, bool interlaced, unsigned char* background);
+
+typedef struct bt_jpeg_error_mgr
+{
+ struct jpeg_error_mgr pub; // "public" fields
+ jmp_buf setjmp_buffer; // for return to caller
+} bt_jpeg_error_mgr;
+
+static void bt_jpeg_error_exit (j_common_ptr cinfo)
+{
+ bt_jpeg_error_mgr* myerr = (bt_jpeg_error_mgr*) cinfo->err;
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message) (cinfo, buffer);
+// MessageBox(NULL, buffer, "JPEG Fatal Error", MB_ICONSTOP);
+ longjmp(myerr->setjmp_buffer, 1);
+}
+
+// stash a scanline
+static void j_putRGBScanline(unsigned char* jpegline, int widthPix, unsigned char* outBuf, int row)
+{
+ int offset = row * widthPix * 3;
+ int count;
+ for (count = 0; count < widthPix; count++)
+ {
+ unsigned char iRed, iBlu, iGrn;
+ unsigned char *oRed, *oBlu, *oGrn;
+
+ iRed = *(jpegline + count * 3 + 0);
+ iGrn = *(jpegline + count * 3 + 1);
+ iBlu = *(jpegline + count * 3 + 2);
+
+ oRed = outBuf + offset + count * 3 + 0;
+ oGrn = outBuf + offset + count * 3 + 1;
+ oBlu = outBuf + offset + count * 3 + 2;
+
+ *oRed = iRed;
+ *oGrn = iGrn;
+ *oBlu = iBlu;
+ }
+}
+
+// stash a gray scanline
+static void j_putGrayScanlineToRGB(unsigned char* jpegline, int widthPix, unsigned char* outBuf, int row)
+{
+ int offset = row * widthPix * 3;
+ int count;
+ for (count = 0; count < widthPix; count++)
+ {
+ unsigned char iGray;
+ unsigned char *oRed, *oBlu, *oGrn;
+
+ // get our grayscale value
+ iGray = *(jpegline + count);
+
+ oRed = outBuf + offset + count * 3;
+ oGrn = outBuf + offset + count * 3 + 1;
+ oBlu = outBuf + offset + count * 3 + 2;
+
+ *oRed = iGray;
+ *oGrn = iGray;
+ *oBlu = iGray;
+ }
+}
+
+static LC_IMAGE* ResizeImage(LC_IMAGE* image)
+{
+ int i, j;
+ long shifted_x, shifted_y;
+ if (image == NULL)
+ return NULL;
+
+ shifted_x = image->width;
+ for (i = 0; ((i < 16) && (shifted_x != 0)); i++)
+ shifted_x = shifted_x >> 1;
+ shifted_x = (i != 0) ? 1 << (i-1) : 1;
+
+ shifted_y = image->height;
+ for (i = 0; ((i < 16) && (shifted_y != 0)); i++)
+ shifted_y = shifted_y >> 1;
+ shifted_y = (i != 0) ? 1 << (i-1) : 1;
+
+ if ((shifted_x == image->width) && (shifted_y == image->height))
+ return image;
+
+ LC_IMAGE* newimage = (LC_IMAGE*)malloc(shifted_x*shifted_y*3+sizeof(LC_IMAGE));
+ newimage->width = (unsigned short)shifted_x;
+ newimage->height = (unsigned short)shifted_y;
+ newimage->bits = (unsigned char*)newimage + sizeof(LC_IMAGE);
+ memset(newimage->bits, 0, shifted_x*shifted_y*3);
+
+ float accumx, accumy;
+ int stx, sty;
+ unsigned char *oldbits = (unsigned char*)image->bits,
+ *newbits = (unsigned char*)newimage->bits;
+
+ for (j = 0; j < image->height; j++)
+ {
+ accumy = (float)newimage->height*j/(float)image->height;
+ sty = (int)floor(accumy);
+
+ for (i = 0; i < image->width; i++)
+ {
+ accumx = (float)newimage->width*i/(float)image->width;
+ stx = (int)floor(accumx);
+
+ newbits[(stx+sty*newimage->width)*3] = oldbits[(i+j*image->width)*3];
+ newbits[(stx+sty*newimage->width)*3+1] = oldbits[(i+j*image->width)*3+1];
+ newbits[(stx+sty*newimage->width)*3+2] = oldbits[(i+j*image->width)*3+2];
+ }
+ }
+
+ free(image);
+ return newimage;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// functions
+
+// Reads a file from disk
+LC_IMAGE* OpenImage(char* filename)
+{
+ char ext[5];
+ if (strlen(filename) != 0)
+ {
+ char *p = strrchr(filename, '.');
+ if (p != NULL)
+ strcpy (ext, p+1);
+ }
+ strlwr(ext);
+
+ if ((strcmp(ext, "jpg") == 0) || (strcmp (ext, "jpeg") == 0))
+ return ResizeImage(OpenJPG(filename));
+ if (strcmp(ext, "bmp") == 0)
+ return ResizeImage(OpenBMP(filename));
+ if ((strcmp (ext, "gif") == 0) || (strcmp (ext, "tmp") == 0))
+ {
+ File file(false);
+ if (!file.Open(filename, "rb"))
+ return NULL;
+ LC_IMAGE* image = ResizeImage(OpenGIF(&file));
+ file.Close();
+ return image;
+ }
+
+// MessageBox (NULL, "Unknown File Format", "Error", MB_ICONSTOP);
+
+ return NULL;
+}
+
+LC_IMAGE* OpenImage(File* file, unsigned char format)
+{
+ if (format != LC_IMAGE_GIF)
+ return NULL;
+ return OpenGIF(file);
+}
+
+bool SaveImage(char* filename, LC_IMAGE* image, LC_IMAGE_OPTS* opts)
+{
+ char ext[5];
+ if (strlen(filename) != 0)
+ {
+ char *p = strrchr(filename, '.');
+ if (p != NULL)
+ strcpy(ext, p+1);
+ }
+ strlwr(ext);
+
+ if ((strcmp (ext, "jpg") == 0) || (strcmp (ext, "jpeg") == 0))
+ return SaveJPG(filename, image, opts->quality, opts->interlaced);
+
+ if (strcmp (ext, "gif") == 0)
+ {
+ File file(false);
+ if (!file.Open(filename, "wb"))
+ return false;
+
+ bool ret = SaveGIF(&file, image, opts->transparent, opts->interlaced, opts->background);
+ file.Close();
+ return ret;
+ }
+
+ if (strcmp (ext, "bmp") == 0)
+ return SaveBMP(filename, image, opts->truecolor == false);
+
+// MessageBox (NULL, "Could not save file", "Error", MB_ICONSTOP);
+
+ return false;
+}
+
+bool SaveImage(File* file, LC_IMAGE* image, LC_IMAGE_OPTS* opts)
+{
+ if (opts->format != LC_IMAGE_GIF)
+ return false;
+ return SaveGIF(file, image, opts->transparent, opts->interlaced, opts->background);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// read functions
+
+static LC_IMAGE* OpenJPG(char* filename)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct bt_jpeg_error_mgr jerr;
+ FILE* infile = NULL;
+ JSAMPARRAY buffer; // Output row buffer
+ int row_stride; // physical row width in output buffer
+
+ if ((infile = fopen(filename, "rb")) == NULL)
+ return NULL;
+
+ cinfo.err = jpeg_std_error(&jerr.pub);
+ jerr.pub.error_exit = bt_jpeg_error_exit;
+
+ if (setjmp(jerr.setjmp_buffer))
+ {
+ jpeg_destroy_decompress(&cinfo);
+
+ if (infile != NULL)
+ fclose(infile);
+ return NULL;
+ }
+
+ jpeg_create_decompress(&cinfo);
+ jpeg_stdio_src(&cinfo, infile);
+ jpeg_read_header(&cinfo, TRUE);
+ jpeg_start_decompress(&cinfo);
+
+ // get our buffer set to hold data
+ LC_IMAGE* image = (LC_IMAGE*)malloc(cinfo.output_width*cinfo.output_height*3 + sizeof(LC_IMAGE));
+
+ if (image == NULL)
+ {
+// MessageBox(NULL, "Error", "Cannot allocate memory", MB_ICONSTOP);
+ jpeg_destroy_decompress(&cinfo);
+ fclose(infile);
+ return NULL;
+ }
+
+ image->width = cinfo.output_width;
+ image->height = cinfo.output_height;
+ image->bits = (char*)image + sizeof(LC_IMAGE);
+
+ row_stride = cinfo.output_width * cinfo.output_components;
+ buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
+
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines(&cinfo, buffer, 1);
+
+ if (cinfo.out_color_components == 3)
+ j_putRGBScanline(buffer[0], image->width, (unsigned char*)image->bits, cinfo.output_scanline-1);
+ else if (cinfo.out_color_components == 1)
+ j_putGrayScanlineToRGB(buffer[0], image->width, (unsigned char*)image->bits, cinfo.output_scanline-1);
+ }
+
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+ fclose(infile);
+
+ return image;
+}
+
+static LC_IMAGE* OpenBMP (char* filename)
+{
+ int bmWidth;
+ int bmHeight;
+ unsigned char bmPlanes;
+ unsigned char bmBitsPixel;
+ typedef struct {
+ unsigned char rgbBlue;
+ unsigned char rgbGreen;
+ unsigned char rgbRed;
+ unsigned char rgbReserved;
+ } RGBQUAD;
+ unsigned char m1,m2;
+ unsigned long sizeimage;
+ short res1,res2;
+ long filesize, pixoff;
+ long bmisize, compression;
+ long xscale, yscale;
+ long colors, impcol;
+ LC_IMAGE* image;
+ unsigned long m_bytesRead = 0;
+ FILE *fp;
+
+ fp = fopen(filename,"rb");
+ if (fp == NULL)
+ return NULL;
+
+ long rc;
+ rc = fread(&m1, 1, 1, fp);
+ m_bytesRead++;
+ if (rc == -1)
+ {
+ fclose(fp);
+ return NULL;
+ }
+
+ rc = fread(&m2, 1, 1, fp);
+ m_bytesRead++;
+ if ((m1!='B') || (m2!='M'))
+ {
+ fclose(fp);
+ return NULL;
+ }
+
+ rc = fread((long*)&(filesize),4,1,fp); m_bytesRead+=4;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ rc = fread((int*)&(res1),2,1,fp); m_bytesRead+=2;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ rc = fread((int*)&(res2),2,1,fp); m_bytesRead+=2;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ rc = fread((long*)&(pixoff),4,1,fp); m_bytesRead+=4;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ rc = fread((long*)&(bmisize),4,1,fp); m_bytesRead+=4;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ rc = fread((long *)&(bmWidth),4,1,fp); m_bytesRead+=4;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ rc = fread((long*)&(bmHeight),4,1,fp); m_bytesRead+=4;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ rc = fread((int*)&(bmPlanes),2,1,fp); m_bytesRead+=2;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ rc = fread((int*)&(bmBitsPixel),2,1,fp); m_bytesRead+=2;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ rc = fread((long*)&(compression),4,1,fp); m_bytesRead+=4;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ rc = fread((long*)&(sizeimage),4,1,fp); m_bytesRead+=4;
+ if (rc != 1) {fclose(fp); return NULL; }
+
+ rc = fread((long*)&(xscale),4,1,fp); m_bytesRead+=4;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ rc = fread((long*)&(yscale),4,1,fp); m_bytesRead+=4;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ rc = fread((long*)&(colors),4,1,fp); m_bytesRead+=4;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ rc = fread((long*)&(impcol),4,1,fp); m_bytesRead+=4;
+ if (rc != 1) { fclose(fp); return NULL; }
+
+ if (colors == 0)
+ colors = 1 << bmBitsPixel;
+
+ RGBQUAD *colormap = NULL;
+
+ if (bmBitsPixel != 24)
+ {
+ colormap = new RGBQUAD[colors];
+ if (colormap == NULL)
+ {
+ fclose(fp);
+ return NULL;
+ }
+
+ int i;
+ for (i = 0; i < colors; i++)
+ {
+ unsigned char r ,g, b, dummy;
+
+ rc = fread(&b, 1, 1, fp);
+ m_bytesRead++;
+ if (rc!=1)
+ {
+ delete [] colormap;
+ fclose(fp);
+ return NULL;
+ }
+
+ rc = fread(&g, 1, 1, fp);
+ m_bytesRead++;
+ if (rc!=1)
+ {
+ delete [] colormap;
+ fclose(fp);
+ return NULL;
+ }
+
+ rc = fread(&r, 1, 1, fp);
+ m_bytesRead++;
+ if (rc != 1)
+ {
+ delete [] colormap;
+ fclose(fp);
+ return NULL;
+ }
+
+
+ rc = fread(&dummy, 1, 1, fp);
+ m_bytesRead++;
+ if (rc != 1)
+ {
+ delete [] colormap;
+ fclose(fp);
+ return NULL;
+ }
+
+ colormap[i].rgbRed=r;
+ colormap[i].rgbGreen=g;
+ colormap[i].rgbBlue=b;
+ }
+ }
+
+ if ((long)m_bytesRead > pixoff)
+ {
+ delete [] colormap;
+ fclose(fp);
+ return NULL;
+ }
+
+ while ((long)m_bytesRead < pixoff)
+ {
+ char dummy;
+ fread(&dummy,1,1,fp);
+ m_bytesRead++;
+ }
+
+ int w = bmWidth;
+ int h = bmHeight;
+
+ // set the output params
+ image = (LC_IMAGE*)malloc(w*h*3 + sizeof(LC_IMAGE));
+ long row_size = w * 3;
+ long bufsize = (long)w * 3 * (long)h;
+
+ if (image != NULL)
+ {
+ image->width = w;
+ image->height = h;
+ image->bits = (char*)image + sizeof(LC_IMAGE);
+ unsigned char* outbuf = (unsigned char*)image->bits;
+ long row = 0;
+ long rowOffset = 0;
+
+ if (compression == 0) // BI_RGB
+ {
+ // read rows in reverse order
+ for (row=bmHeight-1;row>=0;row--)
+ {
+ // which row are we working on?
+ rowOffset = (long unsigned)row*row_size;
+
+ if (bmBitsPixel == 24)
+ {
+ for (int col=0;col<w;col++)
+ {
+ long offset = col * 3;
+ char pixel[3];
+
+ if (fread((void*)(pixel),1,3,fp)==3)
+ {
+ // we swap red and blue here
+ *(outbuf + rowOffset + offset + 0)=pixel[2]; // r
+ *(outbuf + rowOffset + offset + 1)=pixel[1]; // g
+ *(outbuf + rowOffset + offset + 2)=pixel[0]; // b
+ }
+ }
+ m_bytesRead += row_size;
+
+ // read DWORD padding
+ while ((m_bytesRead-pixoff)&3)
+ {
+ char dummy;
+ if (fread(&dummy,1,1,fp) != 1)
+ {
+ free(image);
+ fclose(fp);
+ return NULL;
+ }
+ m_bytesRead++;
+ }
+ }
+ else
+ {
+ // pixels are packed as 1 , 4 or 8 bit vals. need to unpack them
+ int bit_count = 0;
+ unsigned long mask = (1 << bmBitsPixel) - 1;
+ unsigned char inbyte = 0;
+
+ for (int col=0;col<w;col++)
+ {
+ int pix = 0;
+
+ // if we need another byte
+ if (bit_count <= 0)
+ {
+ bit_count = 8;
+ if (fread(&inbyte,1,1,fp) != 1)
+ {
+ free(image);
+ delete [] colormap;
+ fclose(fp);
+ return NULL;
+ }
+ m_bytesRead++;
+ }
+
+ // keep track of where we are in the bytes
+ bit_count -= bmBitsPixel;
+ pix = ( inbyte >> bit_count) & mask;
+
+ // lookup the color from the colormap - stuff it in our buffer
+ // swap red and blue
+ *(outbuf + rowOffset + col * 3 + 2) = colormap[pix].rgbBlue;
+ *(outbuf + rowOffset + col * 3 + 1) = colormap[pix].rgbGreen;
+ *(outbuf + rowOffset + col * 3 + 0) = colormap[pix].rgbRed;
+ }
+
+ // read DWORD padding
+ while ((m_bytesRead-pixoff)&3)
+ {
+ char dummy;
+ if (fread(&dummy,1,1,fp)!=1)
+ {
+ free(image);
+ if (colormap)
+ delete [] colormap;
+ fclose(fp);
+ return NULL;
+ }
+ m_bytesRead++;
+ }
+ }
+ }
+ }
+ else
+ {
+ int i, x = 0;
+ unsigned char c, c1, *pp;
+ row = 0;
+ pp = outbuf + (bmHeight-1)*bmWidth*3;
+
+ if (bmBitsPixel == 8)
+ {
+ while (row < bmHeight)
+ {
+ c = getc(fp);
+
+ if (c)
+ {
+ // encoded mode
+ c1 = getc(fp);
+ for (i = 0; i < c; x++, i++)
+ {
+ *pp = colormap[c1].rgbRed; pp++;
+ *pp = colormap[c1].rgbGreen; pp++;
+ *pp = colormap[c1].rgbBlue; pp++;
+ }
+ }
+ else
+ {
+ // c==0x00, escape codes
+ c = getc(fp);
+
+ if (c == 0x00) // end of line
+ {
+ row++;
+ x = 0;
+ pp = outbuf + (bmHeight-row-1)*bmWidth*3;
+ }
+ else if (c == 0x01)
+ break; // end of pic
+ else if (c == 0x02) // delta
+ {
+ c = getc(fp);
+ x += c;
+ c = getc(fp);
+ row += c;
+ pp = outbuf + x*3 + (bmHeight-row-1)*bmWidth*3;
+ }
+ else // absolute mode
+ {
+ for (i = 0; i < c; x++, i++)
+ {
+ c1 = getc(fp);
+ *pp = colormap[c1].rgbRed; pp++;
+ *pp = colormap[c1].rgbGreen; pp++;
+ *pp = colormap[c1].rgbBlue; pp++;
+ }
+
+ if (c & 1)
+ getc(fp); // odd length run: read an extra pad byte
+ }
+ }
+ }
+ }
+ else if (bmBitsPixel == 4)
+ {
+ while (row < bmHeight)
+ {
+ c = getc(fp);
+
+ if (c)
+ {
+ // encoded mode
+ c1 = getc(fp);
+ for (i = 0; i < c; x++, i++)
+ {
+ *pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbRed; pp++;
+ *pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbGreen; pp++;
+ *pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbBlue; pp++;
+ }
+ }
+ else
+ {
+ // c==0x00, escape codes
+ c = getc(fp);
+
+ if (c == 0x00) // end of line
+ {
+ row++;
+ x = 0;
+ pp = outbuf + (bmHeight-row-1)*bmWidth*3;
+ }
+ else if (c == 0x01)
+ break; // end of pic
+ else if (c == 0x02) // delta
+ {
+ c = getc(fp);
+ x += c;
+ c = getc(fp);
+ row += c;
+ pp = outbuf + x*3 + (bmHeight-row-1)*bmWidth*3;
+ }
+ else // absolute mode
+ {
+ for (i = 0; i < c; x++, i++)
+ {
+ if ((i&1) == 0)
+ c1 = getc(fp);
+ *pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbRed; pp++;
+ *pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbGreen; pp++;
+ *pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbBlue; pp++;
+ }
+
+ if (((c&3) == 1) || ((c&3) == 2))
+ getc(fp); // odd length run: read an extra pad byte
+ }
+ }
+ }
+ }
+ }
+
+ if (colormap)
+ delete [] colormap;
+
+ fclose(fp);
+ }
+
+ return image;
+}
+
+typedef struct
+{
+ unsigned char colormap[3][256];
+
+ // State for GetCode and LZWReadByte
+ char code_buf[256+4];
+ int last_byte; // # of bytes in code_buf
+ int last_bit; // # of bits in code_buf
+ int cur_bit; // next bit index to read
+ bool out_of_blocks; // true if hit terminator data block
+
+ int input_code_size; // codesize given in GIF file
+ int clear_code,end_code;// values for Clear and End codes
+
+ int code_size; // current actual code size
+ int limit_code; // 2^code_size
+ int max_code; // first unused code value
+ bool first_time; // flags first call to LZWReadByte
+
+ // Private state for LZWReadByte
+ int oldcode; // previous LZW symbol
+ int firstcode; // first byte of oldcode's expansion
+
+ // LZW symbol table and expansion stack
+ UINT16 FAR *symbol_head; // => table of prefix symbols
+ UINT8 FAR *symbol_tail; // => table of suffix bytes
+ UINT8 FAR *symbol_stack; // => stack for symbol expansions
+ UINT8 FAR *sp; // stack pointer
+
+ // State for interlaced image processing
+ bool is_interlaced; // true if have interlaced image
+// jvirt_sarray_ptr interlaced_image; // full image in interlaced order
+ unsigned char* interlaced_image;
+ JDIMENSION cur_row_number; // need to know actual row number
+ JDIMENSION pass2_offset; // # of pixel rows in pass 1
+ JDIMENSION pass3_offset; // # of pixel rows in passes 1&2
+ JDIMENSION pass4_offset; // # of pixel rows in passes 1,2,3
+
+ File* input_file;
+ bool first_interlace;
+ unsigned char* buffer;//JSAMPARRAY buffer;
+ unsigned int width, height;
+} gif_source_struct;
+
+typedef gif_source_struct *gif_source_ptr;
+
+// Macros for extracting header data --- note we assume chars may be signed
+#define LM_to_uint(a,b) ((((b)&0xFF) << 8) | ((a)&0xFF))
+#define BitSet(byte, bit) ((byte) & (bit))
+#define INTERLACE 0x40 // mask for bit signifying interlaced image
+#define COLORMAPFLAG 0x80 // mask for bit signifying colormap presence
+
+#undef LZW_TABLE_SIZE
+#define MAX_LZW_BITS 12 // maximum LZW code size
+#define LZW_TABLE_SIZE (1<<MAX_LZW_BITS) // # of possible LZW symbols
+
+static int GetDataBlock (gif_source_ptr sinfo, char *buf)
+{
+ int count = sinfo->input_file->GetChar();
+ if (count > 0)
+ sinfo->input_file->Read(buf, count);
+ return count;
+}
+
+static int GetCode (gif_source_ptr sinfo)
+{
+ register INT32 accum;
+ int offs, ret, count;
+
+ while ( (sinfo->cur_bit + sinfo->code_size) > sinfo->last_bit) {
+ if (sinfo->out_of_blocks)
+ return sinfo->end_code; // fake something useful
+ sinfo->code_buf[0] = sinfo->code_buf[sinfo->last_byte-2];
+ sinfo->code_buf[1] = sinfo->code_buf[sinfo->last_byte-1];
+ if ((count = GetDataBlock(sinfo, &sinfo->code_buf[2])) == 0) {
+ sinfo->out_of_blocks = true;
+ return sinfo->end_code; // fake something useful
+ }
+ sinfo->cur_bit = (sinfo->cur_bit - sinfo->last_bit) + 16;
+ sinfo->last_byte = 2 + count;
+ sinfo->last_bit = sinfo->last_byte * 8;
+ }
+
+ offs = sinfo->cur_bit >> 3; // byte containing cur_bit
+ accum = sinfo->code_buf[offs+2] & 0xFF;
+ accum <<= 8;
+ accum |= sinfo->code_buf[offs+1] & 0xFF;
+ accum <<= 8;
+ accum |= sinfo->code_buf[offs] & 0xFF;
+ accum >>= (sinfo->cur_bit & 7);
+ ret = ((int) accum) & ((1 << sinfo->code_size) - 1);
+
+ sinfo->cur_bit += sinfo->code_size;
+ return ret;
+}
+
+static int LZWReadByte (gif_source_ptr sinfo)
+{
+ register int code; // current working code
+ int incode; // saves actual input code
+
+ // First time, just eat the expected Clear code(s) and return next code,
+ // which is expected to be a raw byte.
+ if (sinfo->first_time)
+ {
+ sinfo->first_time = false;
+ code = sinfo->clear_code; // enables sharing code with Clear case
+ }
+ else
+ {
+ // If any codes are stacked from a previously read symbol, return them
+ if (sinfo->sp > sinfo->symbol_stack)
+ return (int) *(-- sinfo->sp);
+
+ // Time to read a new symbol
+ code = GetCode(sinfo);
+
+ }
+
+ if (code == sinfo->clear_code)
+ {
+ sinfo->code_size = sinfo->input_code_size + 1;
+ sinfo->limit_code = sinfo->clear_code << 1; // 2^code_size
+ sinfo->max_code = sinfo->clear_code + 2; // first unused code value
+ sinfo->sp = sinfo->symbol_stack; // init stack to empty
+ do
+ {
+ code = GetCode(sinfo);
+ } while (code == sinfo->clear_code);
+
+ if (code > sinfo->clear_code)
+ code = 0; // use something valid
+ sinfo->firstcode = sinfo->oldcode = code;
+ return code;
+ }
+
+ if (code == sinfo->end_code)
+ {
+ if (!sinfo->out_of_blocks)
+ {
+ char buf[256];
+ while (GetDataBlock(sinfo, buf) > 0)
+ ; // skip
+ sinfo->out_of_blocks = true;
+ }
+ return 0; // fake something usable
+ }
+
+ incode = code; // save for a moment
+
+ if (code >= sinfo->max_code)
+ {
+ // special case for not-yet-defined symbol
+ // code == max_code is OK; anything bigger is bad data
+ if (code > sinfo->max_code)
+ incode = 0; // prevent creation of loops in symbol table
+ // this symbol will be defined as oldcode/firstcode
+ *(sinfo->sp++) = (UINT8) sinfo->firstcode;
+ code = sinfo->oldcode;
+ }
+
+ while (code >= sinfo->clear_code)
+ {
+ *(sinfo->sp++) = sinfo->symbol_tail[code]; // tail is a byte value
+ code = sinfo->symbol_head[code]; // head is another LZW symbol
+ }
+ sinfo->firstcode = code; // save for possible future use
+
+ if ((code = sinfo->max_code) < LZW_TABLE_SIZE)
+ {
+ sinfo->symbol_head[code] = sinfo->oldcode;
+ sinfo->symbol_tail[code] = (UINT8) sinfo->firstcode;
+ sinfo->max_code++;
+ if ((sinfo->max_code >= sinfo->limit_code) &&
+ (sinfo->code_size < MAX_LZW_BITS)) {
+ sinfo->code_size++;
+ sinfo->limit_code <<= 1; // keep equal to 2^code_size
+ }
+ }
+
+ sinfo->oldcode = incode; // save last input symbol for future use
+ return sinfo->firstcode; // return first byte of symbol's expansion
+}
+
+static LC_IMAGE* OpenGIF(File* file)
+{
+ gif_source_ptr source;
+ source = (gif_source_ptr)malloc (sizeof(gif_source_struct));
+ source->input_file = file;
+
+ char hdrbuf[10];
+ unsigned int width, height;
+ int colormaplen, aspectRatio;
+ int c;
+
+ source->input_file->Read(hdrbuf, 6);
+ if ((hdrbuf[0] != 'G' || hdrbuf[1] != 'I' || hdrbuf[2] != 'F') ||
+ ((hdrbuf[3] != '8' || hdrbuf[4] != '7' || hdrbuf[5] != 'a') &&
+ (hdrbuf[3] != '8' || hdrbuf[4] != '9' || hdrbuf[5] != 'a')))
+ return NULL;
+
+ source->input_file->Read(hdrbuf, 7);
+ width = LM_to_uint(hdrbuf[0],hdrbuf[1]);
+ height = LM_to_uint(hdrbuf[2],hdrbuf[3]);
+ source->height = height;
+ source->width = width;
+ colormaplen = 2 << (hdrbuf[4] & 0x07);
+ aspectRatio = hdrbuf[6] & 0xFF;
+
+ if (BitSet(hdrbuf[4], COLORMAPFLAG))
+ for (int i = 0; i < colormaplen; i++)
+ {
+ source->colormap[0][i] = source->input_file->GetChar();
+ source->colormap[1][i] = source->input_file->GetChar();
+ source->colormap[2][i] = source->input_file->GetChar();
+ }
+
+ for (;;)
+ {
+ c = source->input_file->GetChar();
+
+// if (c == ';')
+// ERREXIT(cinfo, JERR_GIF_IMAGENOTFOUND);
+
+ if (c == '!')
+ {
+ int extlabel = source->input_file->GetChar();
+ char buf[256];
+ while (GetDataBlock(source, buf) > 0)
+ ; // skip
+ continue;
+ }
+
+ if (c != ',')
+ continue;
+
+ source->input_file->Read(hdrbuf, 9);
+ width = LM_to_uint(hdrbuf[4],hdrbuf[5]);
+ height = LM_to_uint(hdrbuf[6],hdrbuf[7]);
+ source->is_interlaced = (hdrbuf[8] & INTERLACE) != 0;
+
+ if (BitSet(hdrbuf[8], COLORMAPFLAG))
+ {
+ colormaplen = 2 << (hdrbuf[8] & 0x07);
+ for (int i = 0; i < colormaplen; i++)
+ {
+ source->colormap[0][i] = source->input_file->GetChar();
+ source->colormap[1][i] = source->input_file->GetChar();
+ source->colormap[2][i] = source->input_file->GetChar();
+ }
+ }
+
+ source->input_code_size = source->input_file->GetChar();
+// if (source->input_code_size < 2 || source->input_code_size >= MAX_LZW_BITS)
+// ERREXIT1(cinfo, JERR_GIF_CODESIZE, source->input_code_size);
+
+ break;
+ }
+
+ source->symbol_head = (UINT16 FAR *) malloc(LZW_TABLE_SIZE * sizeof(UINT16));
+ source->symbol_tail = (UINT8 FAR *) malloc (LZW_TABLE_SIZE * sizeof(UINT8));
+ source->symbol_stack = (UINT8 FAR *) malloc (LZW_TABLE_SIZE * sizeof(UINT8));
+ source->last_byte = 2; // make safe to "recopy last two bytes"
+ source->last_bit = 0; // nothing in the buffer
+ source->cur_bit = 0; // force buffer load on first call
+ source->out_of_blocks = false;
+ source->clear_code = 1 << source->input_code_size;
+ source->end_code = source->clear_code + 1;
+ source->first_time = true;
+ source->code_size = source->input_code_size + 1;
+ source->limit_code = source->clear_code << 1;// 2^code_size
+ source->max_code = source->clear_code + 2; // first unused code value
+ source->sp = source->symbol_stack; // init stack to empty
+
+ if (source->is_interlaced)
+ {
+ source->first_interlace = true;
+ source->interlaced_image = (unsigned char*)malloc(width*height);
+ }
+ else
+ source->first_interlace = false;
+
+ source->buffer = (unsigned char*)malloc(width*3);
+ LC_IMAGE* image = (LC_IMAGE*)malloc(width*height*3 + sizeof(LC_IMAGE));
+ image->width = width;
+ image->height = height;
+ image->bits = (char*)image + sizeof(LC_IMAGE);
+ unsigned char* buf = (unsigned char*)image->bits;
+
+ for (unsigned long scanline = 0; scanline < height; scanline++)
+ {
+ if (source->is_interlaced)
+ {
+ if (source->first_interlace)
+ {
+ register JSAMPROW sptr;
+ register JDIMENSION col;
+ JDIMENSION row;
+
+ for (row = 0; row < source->height; row++)
+ {
+ sptr = &source->interlaced_image[row*source->width];
+ for (col = source->width; col > 0; col--)
+ *sptr++ = (JSAMPLE) LZWReadByte(source);
+ }
+
+ source->first_interlace = false;
+ source->cur_row_number = 0;
+ source->pass2_offset = (source->height + 7) / 8;
+ source->pass3_offset = source->pass2_offset + (source->height + 3) / 8;
+ source->pass4_offset = source->pass3_offset + (source->height + 1) / 4;
+ }
+
+ register int c;
+ register JSAMPROW sptr, ptr;
+ register JDIMENSION col;
+ JDIMENSION irow;
+
+ // Figure out which row of interlaced image is needed, and access it.
+ switch ((int) (source->cur_row_number & 7))
+ {
+ case 0: // first-pass row
+ irow = source->cur_row_number >> 3;
+ break;
+ case 4: // second-pass row
+ irow = (source->cur_row_number >> 3) + source->pass2_offset;
+ break;
+ case 2: // third-pass row
+ case 6:
+ irow = (source->cur_row_number >> 2) + source->pass3_offset;
+ break;
+ default: // fourth-pass row
+ irow = (source->cur_row_number >> 1) + source->pass4_offset;
+ break;
+ }
+ sptr = &source->interlaced_image[irow*source->width];
+ ptr = source->buffer;
+ for (col = source->width; col > 0; col--)
+ {
+ c = GETJSAMPLE(*sptr++);
+ *ptr++ = source->colormap[0][c];
+ *ptr++ = source->colormap[1][c];
+ *ptr++ = source->colormap[2][c];
+ }
+ source->cur_row_number++; // for next time
+ }
+ else
+ {
+ register int c;
+ register JSAMPROW ptr;
+ register JDIMENSION col;
+
+ ptr = source->buffer;
+ for (col = source->width; col > 0; col--)
+ {
+ c = LZWReadByte(source);
+ *ptr++ = source->colormap[0][c];
+ *ptr++ = source->colormap[1][c];
+ *ptr++ = source->colormap[2][c];
+ }
+ }
+
+ memcpy (buf+(width*scanline*3), source->buffer, 3*width);
+ }
+
+ if (source->is_interlaced)
+ free(source->interlaced_image);
+ free(source->buffer);
+ free(source->symbol_head);
+ free(source->symbol_tail);
+ free(source->symbol_stack);
+ free(source);
+
+ return image;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// save functions
+
+static bool SaveJPG (char* filename, LC_IMAGE* image, int quality, bool progressive)
+{
+ struct jpeg_compress_struct cinfo;
+ FILE* outfile = NULL;
+ int row_stride; // physical row widthPix in image buffer
+ struct bt_jpeg_error_mgr jerr;
+
+ // allocate and initialize JPEG compression object
+ cinfo.err = jpeg_std_error(&jerr.pub);
+ jerr.pub.error_exit = bt_jpeg_error_exit;
+
+ if (setjmp(jerr.setjmp_buffer))
+ {
+ jpeg_destroy_compress(&cinfo);
+ if (outfile != NULL)
+ fclose(outfile);
+ return false;
+ }
+
+ jpeg_create_compress(&cinfo);
+ if ((outfile = fopen(filename, "wb")) == NULL)
+ return false;
+
+ jpeg_stdio_dest(&cinfo, outfile);
+
+ cinfo.image_width = image->width;
+ cinfo.image_height = image->height;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, quality, TRUE);
+
+ if (progressive)
+ jpeg_simple_progression(&cinfo);
+
+ jpeg_start_compress(&cinfo, TRUE);
+ row_stride = image->width * 3;
+
+ while (cinfo.next_scanline < cinfo.image_height)
+ {
+ unsigned char* outRow = (unsigned char*)image->bits + (cinfo.next_scanline * image->width * 3);
+ jpeg_write_scanlines(&cinfo, &outRow, 1);
+ }
+
+ jpeg_finish_compress(&cinfo);
+ fclose(outfile);
+ jpeg_destroy_compress(&cinfo);
+
+ return true;
+}
+
+static bool SaveBMP(char* filename, LC_IMAGE* image, bool quantize)
+{
+ FILE *fp;
+ fp = fopen(filename, "wb");
+ if (fp == NULL)
+ return false;
+
+ unsigned short bits;
+ unsigned long cmap, bfSize;
+ unsigned char pal[3][256], *colormappedbuffer;
+
+ if (quantize)
+ {
+ colormappedbuffer = (unsigned char*)malloc(image->width*image->height);
+ dl1quant((unsigned char*)image->bits, colormappedbuffer, image->width, image->height, 256, TRUE, pal);
+ bits = 8;
+ cmap = 256;
+ bfSize = 1078 + image->width*image->height;
+ }
+ else
+ {
+ bits = 24;
+ cmap = 0;
+ bfSize = 54 + image->width*image->height*3;
+ }
+
+ long byteswritten = 0;
+ long pixoff = 54 + cmap*4;
+ short res = 0;
+ char m1 ='B', m2 ='M';
+ fwrite(&m1, 1, 1, fp); byteswritten++; // B
+ fwrite(&m2, 1, 1, fp); byteswritten++; // M
+ fwrite(&bfSize, 4, 1, fp); byteswritten+=4;// bfSize
+ fwrite(&res, 2, 1, fp); byteswritten+=2;// bfReserved1
+ fwrite(&res, 2, 1, fp); byteswritten+=2;// bfReserved2
+ fwrite(&pixoff, 4, 1, fp); byteswritten+=4;// bfOffBits
+
+ unsigned long biSize = 40, compress = 0, size = 0;
+ long width = image->width, height = image->height, pixels = 0;
+ unsigned short planes = 1;
+ fwrite(&biSize, 4, 1, fp); byteswritten+=4;// biSize
+ fwrite(&width, 4, 1, fp); byteswritten+=4;// biWidth
+ fwrite(&height, 4, 1, fp); byteswritten+=4;// biHeight
+ fwrite(&planes, 2, 1, fp); byteswritten+=2;// biPlanes
+ fwrite(&bits, 2, 1, fp); byteswritten+=2;// biBitCount
+ fwrite(&compress, 4, 1, fp);byteswritten+=4;// biCompression
+ fwrite(&size, 4, 1, fp); byteswritten+=4;// biSizeImage
+ fwrite(&pixels, 4, 1, fp); byteswritten+=4;// biXPelsPerMeter
+ fwrite(&pixels, 4, 1, fp); byteswritten+=4;// biYPelsPerMeter
+ fwrite(&cmap, 4, 1, fp); byteswritten+=4;// biClrUsed
+ fwrite(&cmap, 4, 1, fp); byteswritten+=4;// biClrImportant
+
+ if (quantize)
+ {
+ for (int i = 0; i < 256; i++)
+ {
+ putc(pal[2][i], fp);
+ putc(pal[1][i], fp);
+ putc(pal[0][i], fp);
+ putc(0, fp); // dummy
+ }
+
+ for (int row = 0; row < image->height; row++)
+ {
+ int pixbuf;
+
+ for (int col = 0; col < image->width; col++)
+ {
+ int offset = (image->height-row-1) * width + col; // offset into our color-mapped RGB buffer
+ unsigned char pval = *(colormappedbuffer + offset);
+
+ pixbuf = (pixbuf << 8) | pval;
+
+ putc(pixbuf, fp);
+ pixbuf = 0;
+ byteswritten++;
+ }
+
+ // DWORD align
+ while ((byteswritten - pixoff) & 3)
+ {
+ putc(0, fp);
+ byteswritten++;
+ }
+ }
+
+ free(colormappedbuffer);
+ }
+ else
+ {
+ unsigned long widthDW = (((image->width*24) + 31) / 32 * 4);
+ long row, row_size = image->width*3;
+ for (row = 0; row < image->height; row++)
+ {
+ unsigned char* buf = (unsigned char*)image->bits+(image->height-row-1)*row_size;
+
+ // write a row
+ for (int col = 0; col < row_size; col += 3)
+ {
+ putc(buf[col+2], fp);
+ putc(buf[col+1], fp);
+ putc(buf[col], fp);
+ }
+ byteswritten += row_size;
+
+ for (unsigned long count = row_size; count < widthDW; count++)
+ {
+ putc(0, fp); // dummy
+ byteswritten++;
+ }
+ }
+ }
+
+ fclose(fp);
+ return true;
+}
+
+#undef LZW_TABLE_SIZE
+#define MAX_LZW_BITS 12
+typedef INT16 code_int;
+#define LZW_TABLE_SIZE ((code_int) 1 << MAX_LZW_BITS)
+#define HSIZE 5003
+typedef int hash_int;
+#define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1)
+typedef INT32 hash_entry;
+#define HASH_ENTRY(prefix,suffix) ((((hash_entry) (prefix)) << 8) | (suffix))
+
+typedef struct
+{
+ int n_bits;
+ code_int maxcode;
+ int init_bits;
+ INT32 cur_accum;
+ int cur_bits;
+ code_int waiting_code;
+ bool first_byte;
+ code_int ClearCode;
+ code_int EOFCode;
+ code_int free_code;
+ code_int *hash_code;
+ hash_entry FAR *hash_value;
+ int bytesinpkt;
+ char packetbuf[256];
+ File* output_file;
+ void* buffer;//JSAMPARRAY buffer;
+} gif_dest_struct;
+
+typedef gif_dest_struct* gif_dest_ptr;
+
+// Emit a 16-bit word, LSB first
+static void put_word(File* output_file, unsigned int w)
+{
+ output_file->PutChar(w & 0xFF);
+ output_file->PutChar((w >> 8) & 0xFF);
+}
+
+static void flush_packet(gif_dest_ptr dinfo)
+{
+ if (dinfo->bytesinpkt > 0)
+ {
+ dinfo->packetbuf[0] = (char) dinfo->bytesinpkt++;
+ dinfo->output_file->Write(dinfo->packetbuf, dinfo->bytesinpkt);
+ dinfo->bytesinpkt = 0;
+ }
+}
+
+static void output(gif_dest_ptr dinfo, code_int code)
+{
+ dinfo->cur_accum |= ((INT32) code) << dinfo->cur_bits;
+ dinfo->cur_bits += dinfo->n_bits;
+
+ while (dinfo->cur_bits >= 8)
+ {
+ (dinfo)->packetbuf[++(dinfo)->bytesinpkt] = (char) (dinfo->cur_accum & 0xFF);
+ if ((dinfo)->bytesinpkt >= 255)
+ flush_packet(dinfo);
+
+ dinfo->cur_accum >>= 8;
+ dinfo->cur_bits -= 8;
+ }
+
+ if (dinfo->free_code > dinfo->maxcode) {
+ dinfo->n_bits++;
+ if (dinfo->n_bits == MAX_LZW_BITS)
+ dinfo->maxcode = LZW_TABLE_SIZE;
+ else
+ dinfo->maxcode = MAXCODE(dinfo->n_bits);
+ }
+}
+
+// Accept and compress one 8-bit byte
+static void compress_byte (gif_dest_ptr dinfo, int c)
+{
+ register hash_int i;
+ register hash_int disp;
+ register hash_entry probe_value;
+
+ if (dinfo->first_byte) {
+ dinfo->waiting_code = c;
+ dinfo->first_byte = FALSE;
+ return;
+ }
+
+ i = ((hash_int) c << (MAX_LZW_BITS-8)) + dinfo->waiting_code;
+ if (i >= HSIZE)
+ i -= HSIZE;
+
+ probe_value = HASH_ENTRY(dinfo->waiting_code, c);
+
+ if (dinfo->hash_code[i] != 0) {
+ if (dinfo->hash_value[i] == probe_value) {
+ dinfo->waiting_code = dinfo->hash_code[i];
+ return;
+ }
+ if (i == 0)
+ disp = 1;
+ else
+ disp = HSIZE - i;
+ for (;;) {
+ i -= disp;
+ if (i < 0)
+ i += HSIZE;
+ if (dinfo->hash_code[i] == 0)
+ break;
+ if (dinfo->hash_value[i] == probe_value) {
+ dinfo->waiting_code = dinfo->hash_code[i];
+ return;
+ }
+ }
+ }
+
+ output(dinfo, dinfo->waiting_code);
+ if (dinfo->free_code < LZW_TABLE_SIZE)
+ {
+ dinfo->hash_code[i] = dinfo->free_code++;
+ dinfo->hash_value[i] = probe_value;
+ }
+ else
+ {
+ memset(dinfo->hash_code, 0, HSIZE * sizeof(code_int));
+ dinfo->free_code = dinfo->ClearCode + 2;
+ output(dinfo, dinfo->ClearCode);
+ dinfo->n_bits = dinfo->init_bits;
+ dinfo->maxcode = MAXCODE(dinfo->n_bits);
+ }
+ dinfo->waiting_code = c;
+}
+
+static bool SaveGIF(File* file, LC_IMAGE* image, bool transparent, bool interlaced, unsigned char* background)
+{
+ int InitCodeSize, FlagByte;
+ int i;
+
+ unsigned char pal[3][256];
+ unsigned char* colormappedbuffer = (unsigned char*)malloc(image->width*image->height);
+ dl1quant((unsigned char*)image->bits, colormappedbuffer, image->width, image->height, 256, true, pal);
+
+ gif_dest_ptr dinfo;
+ dinfo = (gif_dest_ptr) malloc (sizeof(gif_dest_struct));
+ dinfo->output_file = file;
+ dinfo->buffer = malloc(image->width*sizeof(JDIMENSION));
+ dinfo->hash_code = (code_int*) malloc(HSIZE * sizeof(code_int));
+ dinfo->hash_value = (hash_entry FAR*)malloc(HSIZE*sizeof(hash_entry));
+
+ InitCodeSize = 8;
+ // Write the GIF header.
+ file->PutChar('G');
+ file->PutChar('I');
+ file->PutChar('F');
+ file->PutChar('8');
+ file->PutChar(transparent ? '9' : '7');
+ file->PutChar('a');
+ // Write the Logical Screen Descriptor
+ put_word(file, (unsigned int)image->width);
+ put_word(file, (unsigned int)image->height);
+ FlagByte = 0x80;
+ FlagByte |= (7) << 4; // color resolution
+ FlagByte |= (7); // size of global color table
+ file->PutChar(FlagByte);
+ file->PutChar(0); // Background color index
+ file->PutChar(0); // Reserved (aspect ratio in GIF89)
+ // Write the Global Color Map
+ for (i = 0; i < 256; i++)
+ {
+ file->PutChar(pal[0][i]);
+ file->PutChar(pal[1][i]);
+ file->PutChar(pal[2][i]);
+ }
+
+ // Write out extension for transparent colour index, if necessary.
+ if (transparent)
+ {
+ unsigned char index = 0;
+
+ for (i = 0; i < 256; i++)
+ if (background[0] == pal[0][i] &&
+ background[1] == pal[1][i] &&
+ background[2] == pal[2][i])
+ {
+ index = i;
+ break;
+ }
+
+ file->PutChar('!');
+ file->PutChar(0xf9);
+ file->PutChar(4);
+ file->PutChar(1);
+ file->PutChar(0);
+ file->PutChar(0);
+ file->PutChar(index);
+ file->PutChar(0);
+ }
+
+ // Write image separator and Image Descriptor
+ file->PutChar(',');
+ put_word(file, 0);
+ put_word(file, 0);
+ put_word(file, (unsigned int)image->width);
+ put_word(file, (unsigned int)image->height);
+ // flag byte: interlaced
+ if (interlaced)
+ file->PutChar(0x40);
+ else
+ file->PutChar(0x00);
+ file->PutChar(InitCodeSize);// Write Initial Code Size byte
+
+ // Initialize for LZW compression of image data
+ dinfo->n_bits = dinfo->init_bits = InitCodeSize+1;
+ dinfo->maxcode = MAXCODE(dinfo->n_bits);
+ dinfo->ClearCode = ((code_int) 1 << (InitCodeSize));
+ dinfo->EOFCode = dinfo->ClearCode + 1;
+ dinfo->free_code = dinfo->ClearCode + 2;
+ dinfo->first_byte = TRUE;
+ dinfo->bytesinpkt = 0;
+ dinfo->cur_accum = 0;
+ dinfo->cur_bits = 0;
+ memset(dinfo->hash_code, 0, HSIZE * sizeof(code_int));
+ output(dinfo, dinfo->ClearCode);
+
+ int scanline = 0;
+ int pass = 0;
+ while (scanline < image->height)
+ {
+ memcpy(dinfo->buffer, colormappedbuffer+(scanline*image->width), image->width);
+
+ register JSAMPROW ptr;
+ register JDIMENSION col;
+
+ ptr = (unsigned char*)dinfo->buffer;
+ for (col = image->width; col > 0; col--)
+ compress_byte(dinfo, GETJSAMPLE(*ptr++));
+
+ if (interlaced)
+ {
+ switch (pass)
+ {
+ case 0:
+ {
+ scanline += 8;
+ if (scanline >= image->height)
+ {
+ pass++;
+ scanline = 4;
+ }
+ } break;
+
+ case 1:
+ {
+ scanline += 8;
+ if (scanline >= image->height)
+ {
+ pass++;
+ scanline = 2;
+ }
+ } break;
+
+ case 2:
+ {
+ scanline += 4;
+ if (scanline >= image->height)
+ {
+ pass++;
+ scanline = 1;
+ }
+ } break;
+
+ case 3:
+ {
+ scanline += 2;
+ } break;
+ }
+ }
+ else
+ scanline++;
+ }
+
+ // Finish up at the end of the file.
+ if (!dinfo->first_byte)
+ output(dinfo, dinfo->waiting_code);
+ output(dinfo, dinfo->EOFCode);
+ if (dinfo->cur_bits > 0)
+ {
+ (dinfo)->packetbuf[++(dinfo)->bytesinpkt] = (char) (dinfo->cur_accum & 0xFF);
+ if ((dinfo)->bytesinpkt >= 255)
+ flush_packet(dinfo);
+ }
+
+ flush_packet(dinfo);
+ file->PutChar(0);
+ file->PutChar(';');
+ file->Flush();
+
+ free(dinfo->buffer);
+ free(dinfo->hash_code);
+ free(dinfo->hash_value);
+ free(dinfo);
+ free(colormappedbuffer);
+ return true;
+}
+
+#ifdef _WINDOWS
+//#include <windows.h>
+//#include <vfw.h>
+
+#define AVIIF_KEYFRAME 0x00000010L // this frame is a key frame.
+
+void SaveVideo(char* filename, LC_IMAGE** images, int count, float fps)
+{
+/*
+ AVISTREAMINFO strhdr;
+ PAVIFILE pfile = NULL;
+ PAVISTREAM ps = NULL, psCompressed = NULL;
+ AVICOMPRESSOPTIONS opts;
+ AVICOMPRESSOPTIONS FAR * aopts[1] = { &opts };
+ _fmemset(&opts, 0, sizeof(opts));
+
+ // first let's make sure we are running on 1.1
+ WORD wVer = HIWORD(VideoForWindowsVersion());
+ if (wVer < 0x010a)
+ {
+ AfxMessageBox("Video for Windows 1.1 or later required", MB_OK|MB_ICONSTOP);
+ return;
+ }
+
+ AVIFileInit();
+
+ if (AVIFileOpen(&pfile, fn, OF_WRITE | OF_CREATE, NULL) == AVIERR_OK)
+ {
+ // Fill in the header for the video stream.
+ _fmemset(&strhdr, 0, sizeof(strhdr));
+ strhdr.fccType = streamtypeVIDEO;
+ strhdr.fccHandler = 0;
+ strhdr.dwScale = 1;
+/////////////// set FPS
+ strhdr.dwRate = 15; // 15 fps
+ strhdr.dwSuggestedBufferSize = plpbi[0]->biSizeImage;
+ SetRect(&strhdr.rcFrame, 0, 0, (int) plpbi[0]->biWidth, (int) plpbi[0]->biHeight);
+
+ // And create the stream.
+ if (AVIFileCreateStream(pfile, &ps, &strhdr) == AVIERR_OK)
+ if (AVISaveOptions(AfxGetMainWnd()->m_hWnd, 0, 1, &ps, (LPAVICOMPRESSOPTIONS FAR *) &aopts))
+ if (AVIMakeCompressedStream(&psCompressed, ps, &opts, NULL) == AVIERR_OK)
+ if (AVIStreamSetFormat(psCompressed, 0, plpbi[0], plpbi[0]->biSize + plpbi[0]->biClrUsed * sizeof(RGBQUAD)) == AVIERR_OK)
+ {
+ float fPause = (float)AfxGetApp()->GetProfileInt("Default", "AVI Pause", 100)/100;
+ int time = (int)(fPause * 15);
+///////////// set FPS
+ time = 1;
+
+ for (int i = 0; i < nCount; i++)
+ {
+ if (AVIStreamWrite(psCompressed, i * time, 1,
+ (LPBYTE) plpbi[i] + plpbi[i]->biSize + plpbi[i]->biClrUsed * sizeof(RGBQUAD),
+ plpbi[i]->biSizeImage, i%5 ? 0 : AVIIF_KEYFRAME, NULL, NULL) != AVIERR_OK)
+ break;
+ }
+ }
+ }
+
+ // Now close the file
+ if (ps) AVIStreamClose(ps);
+ if (psCompressed) AVIStreamClose(psCompressed);
+ if (pfile) AVIFileClose(pfile);
+ AVIFileExit();
+*/
+}
+#else
+void SaveVideo(char* filename, LC_IMAGE** images, int count, float fps)
+{
+ // SystemDoMessageBox("Format not supported under this platform.", LC_MB_OK|LC_MB_ERROR);
+}
+#endif
diff --git a/common/image.h b/common/image.h
new file mode 100644
index 0000000..32a76de
--- /dev/null
+++ b/common/image.h
@@ -0,0 +1,14 @@
+#ifndef _IMAGE_H_
+#define _IMAGE_H_
+
+class File;
+
+#include "typedefs.h"
+
+LC_IMAGE* OpenImage(char* filename);
+LC_IMAGE* OpenImage(File* file, unsigned char format);
+bool SaveImage(File* file, LC_IMAGE* image, LC_IMAGE_OPTS* opts);
+bool SaveImage(char* filename, LC_IMAGE* image, LC_IMAGE_OPTS* opts);
+void SaveVideo(char* filename, LC_IMAGE** images, int count, float fps);
+
+#endif // _IMAGE_H_ \ No newline at end of file
diff --git a/common/light.cpp b/common/light.cpp
new file mode 100644
index 0000000..f634822
--- /dev/null
+++ b/common/light.cpp
@@ -0,0 +1,382 @@
+// Light object.
+
+#ifdef _WINDOWS
+#include "stdafx.h"
+#endif
+#include <stdlib.h>
+#include "boundbox.h"
+#include "light.h"
+#include "defines.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// Camera construction/destruction
+
+Light::Light()
+{
+ m_BoundingBox.Initialize(this, LC_LIGHT);
+ m_TargetBoundingBox.Initialize(this, LC_LIGHT_TARGET);
+ m_pNext = NULL;
+ m_nState = 0;
+}
+
+Light::~Light()
+{
+ RemoveKeys();
+}
+
+void Light::RemoveKeys()
+{
+
+}
+
+void Light::MinIntersectDist(CLICKLINE* pLine)
+{
+ double dist;
+
+ if (m_nState & LC_LIGHT_HIDDEN)
+ return;
+
+ dist = m_BoundingBox.FindIntersectDist(pLine);
+
+ if (dist < pLine->mindist)
+ {
+ pLine->mindist = dist;
+ pLine->pClosest = &m_BoundingBox;
+ }
+
+ dist = m_TargetBoundingBox.FindIntersectDist(pLine);
+
+ if (dist < pLine->mindist)
+ {
+ pLine->mindist = dist;
+ pLine->pClosest = &m_TargetBoundingBox;
+ }
+}
+
+void Light::UpdatePosition(unsigned short nTime, bool bAnimation)
+{
+
+}
+
+/*
+ glNewList (LC_LIGHT_LIST, GL_COMPILE);
+ float radius = 0.2f;
+ int slices = 6, stacks = 6;
+ float rho, drho, theta, dtheta;
+ float x, y, z;
+ int j, imin, imax;
+ drho = 3.1415926536f/(float)stacks;
+ dtheta = 2.0f*3.1415926536f/(float)slices;
+
+ // draw +Z end as a triangle fan
+ glBegin(GL_TRIANGLE_FAN);
+ glVertex3f(0.0, 0.0, radius);
+ for (j = 0; j <= slices; j++)
+ {
+ theta = (j == slices) ? 0.0f : j * dtheta;
+ x = (float)(-sin(theta) * sin(drho));
+ y = (float)(cos(theta) * sin(drho));
+ z = (float)(cos(drho));
+ glVertex3f(x*radius, y*radius, z*radius);
+ }
+ glEnd();
+
+ imin = 1;
+ imax = stacks-1;
+
+ for (i = imin; i < imax; i++)
+ {
+ rho = i * drho;
+ glBegin(GL_QUAD_STRIP);
+ for (j = 0; j <= slices; j++)
+ {
+ theta = (j == slices) ? 0.0f : j * dtheta;
+ x = (float)(-sin(theta) * sin(rho));
+ y = (float)(cos(theta) * sin(rho));
+ z = (float)(cos(rho));
+ glVertex3f(x*radius, y*radius, z*radius);
+ x = (float)(-sin(theta) * sin(rho+drho));
+ y = (float)(cos(theta) * sin(rho+drho));
+ z = (float)(cos(rho+drho));
+ glVertex3f(x*radius, y*radius, z*radius);
+ }
+ glEnd();
+ }
+
+ // draw -Z end as a triangle fan
+ glBegin(GL_TRIANGLE_FAN);
+ glVertex3f(0.0, 0.0, -radius);
+ rho = 3.1415926536f - drho;
+ for (j = slices; j >= 0; j--)
+ {
+ theta = (j==slices) ? 0.0f : j * dtheta;
+ x = (float)(-sin(theta) * sin(rho));
+ y = (float)(cos(theta) * sin(rho));
+ z = (float)(cos(rho));
+ glVertex3f(x*radius, y*radius, z*radius);
+ }
+ glEnd();
+ glEndList();
+
+ glNewList (LC_TARGET_LIST, GL_COMPILE);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ float box[24][3] = { { 0.2f, 0.2f, 0.2f },
+ { -0.2f, 0.2f, 0.2f }, { -0.2f, -0.2f, 0.2f }, { 0.2f, -0.2f, 0.2f },
+ { 0.2f, 0.2f, -0.2f }, { 0.2f, -0.2f, -0.2f }, { -0.2f, -0.2f, -0.2f },
+ { -0.2f, 0.2f, -0.2f }, { -0.2f, -0.2f, 0.2f }, { -0.2f, 0.2f, 0.2f },
+ { -0.2f, 0.2f, -0.2f }, { -0.2f, -0.2f, -0.2f },{ 0.2f, -0.2f, -0.2f },
+ { 0.2f, 0.2f, -0.2f }, { 0.2f, 0.2f, 0.2f }, { 0.2f, -0.2f, 0.2f },
+ { 0.2f, 0.2f, -0.2f }, { -0.2f, 0.2f, -0.2f }, { -0.2f, 0.2f, 0.2f },
+ { 0.2f, 0.2f, 0.2f }, { 0.2f, -0.2f, 0.2f }, { -0.2f, -0.2f, 0.2f },
+ { -0.2f, -0.2f, -0.2f },{ 0.2f, -0.2f, -0.2f } };
+ glVertexPointer (3, GL_FLOAT, 0, box);
+ glDrawArrays(GL_QUADS, 0, 24);
+ glEndList();
+*/
+
+
+/*
+typedef enum { LK_POSITION, LK_TARGET } LK_TYPES;
+
+typedef struct LIGHT_KEY {
+ WORD time;
+ float param[3];
+ BYTE type;
+ LIGHT_KEY *next;
+} LIGHT_KEY;
+
+class CLight
+{
+public:
+ CLight();
+ CLight(float px, float py, float pz);
+ ~CLight();
+
+protected:
+ // Position
+ LIGHT_KEY *m_Instructions;
+ LIGHT_KEY *m_Animator;
+
+ BOOL m_bDirectional;
+
+public:
+ void Render();
+ void CalculateBoundingBox(WORD nTime, BOOL bAnimator);
+ void CalculatePosition(WORD nTime, BOOL bAnimator, float pos[3], float target[3]);
+ void ChangeKey(WORD nTime, BOOL bAnimator, BOOL bAddKey, float param[3], int nKeyType);
+ void UpdateInformation(WORD nTime, BOOL bAnimator);
+ void InterpolateKey(LIGHT_KEY* pStart, LIGHT_KEY* pEnd, WORD nTime, float key[3]);
+
+ float m_fPosition[4];
+};
+
+static LIGHT_KEY* AddNode (LIGHT_KEY *node, WORD nTime, BYTE nType)
+{
+ LIGHT_KEY* newnode = (LIGHT_KEY*)malloc(sizeof(LIGHT_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] = 0;
+
+ return newnode;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+// new positional light
+CLight::CLight(float px, float py, float pz)
+{
+ m_bHidden = FALSE;
+ m_bSelected = TRUE;
+ m_bFocused = TRUE;
+ m_bTargetSelected = FALSE;
+ m_bTargetFocused = FALSE;
+ m_bDirectional = FALSE;
+ m_bEnabled = TRUE;
+
+ m_Animator = AddNode (NULL, 1, LK_POSITION);
+ m_Animator->param[0] = px;
+ m_Animator->param[1] = py;
+ m_Animator->param[2] = pz;
+
+ m_Instructions = AddNode (NULL, 1, LK_POSITION);
+ m_Instructions->param[0] = px;
+ m_Instructions->param[1] = py;
+ m_Instructions->param[2] = pz;
+
+ m_fPosition[0] = px;
+ m_fPosition[1] = py;
+ m_fPosition[2] = pz;
+ m_fPosition[3] = 1;
+}
+
+void CLight::MinIntersectDist(CLICKLINE* Line)
+{
+ m_PositionBox.MinIntersectDist(Line, FALSE);
+ if (m_bDirectional)
+ m_TargetBox.MinIntersectDist(Line, FALSE);
+}
+
+void CLight::CalculateBoundingBox(WORD nTime, BOOL bAnimator)
+{
+ float pos[3], target[3];
+ CalculatePosition(nTime, bAnimator, pos, target);
+ m_PositionBox.CalculateBoundingBox(pos);
+ if (m_bDirectional)
+ m_TargetBox.CalculateBoundingBox(target);
+}
+
+void CLight::RemoveKeys()
+{
+ LIGHT_KEY *node = m_Instructions;
+
+ while (node)
+ {
+ LIGHT_KEY *prev = node;
+ node = node->next;
+ free (prev);
+ }
+
+ node = m_Animator;
+
+ while (node)
+ {
+ LIGHT_KEY *prev = node;
+ node = node->next;
+ free (prev);
+ }
+}
+
+void CLight::CalculatePosition(WORD nTime, BOOL bAnimator, float pos[3], float target[3])
+{
+ LIGHT_KEY *node, *pp = NULL, *np = NULL, *pt = NULL, *nt = NULL;
+ if (bAnimator)
+ node = m_Animator;
+ else
+ node = m_Instructions;
+
+ while (node && (!np || !nt))
+ {
+ if (node->time <= nTime)
+ {
+ if (node->type == LK_POSITION)
+ pp = node;
+ else
+ pt = node;
+ }
+ else
+ {
+ if (node->type == LK_POSITION)
+ {
+ if (np == NULL)
+ np = node;
+ }
+ else
+ {
+ if (nt == NULL)
+ nt = node;
+ }
+ }
+
+ node = node->next;
+ }
+
+ if (bAnimator && (np != NULL) && (pp->time != nTime))
+ InterpolateKey(pp, np, nTime, pos);
+ else
+ memcpy (pos, pp->param, sizeof(float[3]));
+
+ if (m_bDirectional)
+ {
+ if (bAnimator && (nt != NULL) && (pt->time != nTime))
+ InterpolateKey(pt, nt, nTime, target);
+ else
+ memcpy (target, pt->param, sizeof(float[3]));
+ }
+}
+
+void CLight::InterpolateKey(LIGHT_KEY* pStart, LIGHT_KEY* pEnd, WORD nTime, float key[3])
+{
+// USE KEY IN/OUT WEIGHTS
+ float t = (float)(nTime - pStart->time)/(pEnd->time - pStart->time);
+ key[0] = pStart->param[0] + (pEnd->param[0] - pStart->param[0])*t;
+ key[1] = pStart->param[1] + (pEnd->param[1] - pStart->param[1])*t;
+ key[2] = pStart->param[2] + (pEnd->param[2] - pStart->param[2])*t;
+}
+
+void CLight::ChangeKey(WORD nTime, BOOL bAnimator, BOOL bAddKey, float param[3], int nKeyType)
+{
+ LIGHT_KEY *node, *poskey = NULL, *newpos = NULL;
+ if (bAnimator)
+ node = m_Animator;
+ else
+ node = m_Instructions;
+
+ 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;
+
+ newpos->param[0] = param[0];
+ newpos->param[1] = param[1];
+ newpos->param[2] = param[2];
+}
+
+void CLight::UpdateInformation(WORD nTime, BOOL bAnimator)
+{
+ if (m_bDirectional)
+ {
+///////
+ m_fPosition[3] = 0;
+ }
+ else
+ {
+ CalculatePosition(nTime, bAnimator, m_fPosition, NULL);
+ m_fPosition[3] = 1;
+ }
+// CalculateBoundingBox();
+}
+
+void CLight::Render()
+{
+ if (m_bDirectional)
+ {
+ }
+ else
+ {
+ // Draw a small ball at the light source.
+ glPushMatrix();
+ glTranslatef(m_fPosition[0], m_fPosition[1], m_fPosition[2]);
+ glCallList(LC_LIGHT_LIST);
+ glPopMatrix();
+ }
+}
+*/
diff --git a/common/light.h b/common/light.h
new file mode 100644
index 0000000..bd656d2
--- /dev/null
+++ b/common/light.h
@@ -0,0 +1,53 @@
+//
+// light.h
+////////////////////////////////////////////////////
+
+#ifndef _LIGHT_H_
+#define _LIGHT_H_
+
+#define LC_LIGHT_HIDDEN 0x01
+#define LC_LIGHT_SELECTED 0x02
+#define LC_LIGHT_FOCUSED 0x04
+#define LC_LIGHT_TARGET_SELECTED 0x08
+#define LC_LIGHT_TARGET_FOCUSED 0x10
+#define LC_LIGHT_ENABLED 0x20
+
+class Light
+{
+public:
+ Light();
+ ~Light();
+
+ Light* m_pNext;
+
+ bool IsVisible()
+ { return (m_nState & LC_LIGHT_HIDDEN) == 0; }
+ bool IsSelected()
+ { return (m_nState & (LC_LIGHT_SELECTED|LC_LIGHT_TARGET_SELECTED)) != 0; }
+ void Select()
+ { m_nState |= (LC_LIGHT_SELECTED|LC_LIGHT_TARGET_SELECTED); }
+ void UnSelect()
+ { m_nState &= ~(LC_LIGHT_SELECTED|LC_LIGHT_FOCUSED|LC_LIGHT_TARGET_SELECTED|LC_LIGHT_TARGET_FOCUSED); }
+ void UnFocus()
+ { m_nState &= ~(LC_LIGHT_FOCUSED|LC_LIGHT_TARGET_FOCUSED); }
+ void FocusEye()
+ { m_nState |= (LC_LIGHT_FOCUSED|LC_LIGHT_SELECTED); }
+ void FocusTarget()
+ { m_nState |= (LC_LIGHT_TARGET_FOCUSED|LC_LIGHT_TARGET_SELECTED); }
+ const char* GetName()
+ { return m_strName; }
+
+ void MinIntersectDist(CLICKLINE* Line);
+ void UpdatePosition(unsigned short nTime, bool bAnimation);
+
+protected:
+ void RemoveKeys();
+
+ BoundingBox m_BoundingBox;
+ BoundingBox m_TargetBoundingBox;
+
+ unsigned char m_nState;
+ char m_strName[81];
+};
+
+#endif // _LIGHT_H_
diff --git a/common/matrix.cpp b/common/matrix.cpp
new file mode 100644
index 0000000..829cd6f
--- /dev/null
+++ b/common/matrix.cpp
@@ -0,0 +1,643 @@
+// Matrix class
+//
+
+#include <memory.h>
+#include <math.h>
+#include "matrix.h"
+#include "file.h"
+#include "defines.h"
+
+static float Identity[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
+
+// Perform a 4x4 matrix multiplication (product = a x b).
+// WARNING: (product != b) assumed
+static void matmul(float *product, const float *a, const float *b)
+{
+ int i;
+
+// #define M(row,col) m[col*4+row]
+#define A(row,col) a[(col<<2)+row]
+#define B(row,col) b[(col<<2)+row]
+#define P(row,col) product[(col<<2)+row]
+
+ for (i = 0; i < 4; i++)
+ {
+ float ai0=A(i,0), ai1=A(i,1), ai2=A(i,2), ai3=A(i,3);
+ P(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0);
+ P(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1);
+ P(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2);
+ P(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3);
+ }
+
+#undef A
+#undef B
+#undef P
+}
+
+// Generate a 4x4 transformation matrix from rotation parameters.
+static void rotation_matrix(double angle, float x, float y, float z, float m[] )
+{
+ float s, c, mag, xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
+
+ s = (float)sin (angle * DTOR);
+ c = (float)cos (angle * DTOR);
+ mag = (float)sqrt(x*x + y*y + z*z);
+
+ if (mag == 0)
+ {
+ // generate an identity matrix and return
+ memcpy(m, Identity, sizeof(float[16]));
+ return;
+ }
+
+ x /= mag;
+ y /= mag;
+ z /= mag;
+
+ xx = x * x;
+ yy = y * y;
+ zz = z * z;
+ xy = x * y;
+ yz = y * z;
+ zx = z * x;
+ xs = x * s;
+ ys = y * s;
+ zs = z * s;
+ one_c = 1.0f - c;
+
+ m[0] = (one_c * xx) + c;
+ m[4] = (one_c * xy) - zs;
+ m[8] = (one_c * zx) + ys;
+ m[12]= 0;
+
+ m[1] = (one_c * xy) + zs;
+ m[5] = (one_c * yy) + c;
+ m[9] = (one_c * yz) - xs;
+ m[13]= 0;
+
+ m[2] = (one_c * zx) - ys;
+ m[6] = (one_c * yz) + xs;
+ m[10]= (one_c * zz) + c;
+ m[14]= 0;
+
+ m[3] = 0;
+ m[7] = 0;
+ m[11]= 0;
+ m[15]= 1;
+}
+/*
+double PointDistance (double x1, double y1, double z1, double x2, double y2, double z2)
+{
+ return sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)+(z2-z1)*(z2-z1));
+}
+*/
+Matrix::Matrix()
+{
+ LoadIdentity();
+}
+
+// Expand from the .bin file
+Matrix::Matrix(float* floats)
+{
+ m[0] = floats[0];
+ m[1] = floats[1];
+ m[2] = floats[2];
+ m[3] = 0.0f;
+ m[4] = floats[3];
+ m[5] = floats[4];
+ m[6] = floats[5];
+ m[7] = 0.0f;
+ m[8] = floats[6];
+ m[9] = floats[7];
+ m[10] = floats[8];
+ m[11] = 0.0f;
+ m[12] = floats[9];
+ m[13] = floats[10];
+ m[14] = floats[11];
+ m[15] = 0.0f;
+}
+
+Matrix::Matrix(double mat[16])
+{
+ for (int i = 0; i < 16; i++)
+ m[i] = (float)mat[i];
+}
+
+// Create a matrix from axis-angle and a point
+Matrix::Matrix(float rot[4], float pos[3])
+{
+ float tmp[4] = { rot[0], rot[1], rot[2], rot[3]*DTOR };
+ float q[4];
+ float length, cosA, sinA;
+ length = (float)sqrt(tmp[0]*tmp[0] + tmp[1]*tmp[1] + tmp[2]*tmp[2]);
+
+ // if zero vector passed in, just return identity quaternion
+ if (length < 1E-5)
+ {
+ q[0] = 0;
+ q[1] = 0;
+ q[2] = 0;
+ q[3] = 1;
+ return;
+ }
+
+ tmp[0] /= length;
+ tmp[1] /= length;
+ tmp[2] /= length;
+
+ cosA = (float)cos(tmp[3] / 2.0f);
+ sinA = (float)sin(tmp[3] / 2.0f);
+
+ q[3] = cosA;
+ q[0] = sinA * tmp[0];
+ q[1] = sinA * tmp[1];
+ q[2] = sinA * tmp[2];
+
+ // Now calculate the matrix
+ float s,xs,ys,zs,wx,wy,wz,xx,xy,xz,yy,yz,zz;
+
+ s = 2.0f / (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
+
+ xs = q[0] * s; ys = q[1] * s; zs = q[2] * s;
+ wx = q[3] * xs; wy = q[3] * ys; wz = q[3] * zs;
+ xx = q[0] * xs; xy = q[0] * ys; xz = q[0] * zs;
+ yy = q[1] * ys; yz = q[1] * zs; zz = q[2] * zs;
+
+ m[0] = 1.0f - (yy + zz);
+ m[4] = xy - wz;
+ m[8] = xz + wy;
+ m[12]= pos[0];
+
+ m[1] = xy + wz;
+ m[5] = 1.0f - (xx + zz);
+ m[9] = yz - wx;
+ m[13]= pos[1];
+
+ m[2] = xz - wy;
+ m[6] = yz + wx;
+ m[10]= 1.0f - (xx + yy);
+ m[14]= pos[2];
+
+ m[3] = 0.0f;
+ m[7] = 0.0f;
+ m[11] = 0.0f;
+ m[15] = 1.0f;
+}
+
+Matrix::~Matrix()
+{
+}
+
+void Matrix::FromFloat(float* mat)
+{
+ memcpy (&m[0], mat, sizeof(float[16]));
+}
+
+void Matrix::LoadIdentity()
+{
+ memcpy (&m[0], &Identity, sizeof(float[16]));
+}
+
+void Matrix::Multiply(Matrix& m1, Matrix& m2)
+{
+ matmul (m, m1.m, m2.m);
+}
+
+void Matrix::Rotate(float angle, float x, float y, float z)
+{
+ if (angle == 0.0) return;
+ float rm[16];
+ rotation_matrix(angle, x, y, z, rm);
+ matmul(rm, rm, m);
+ memcpy (&m[0], &rm[0], sizeof(rm));
+
+ for (int i = 0; i < 12; i++)
+ if (fabs (m[i]) < .001f)
+ m[i] = 0;
+}
+
+void Matrix::RotateCenter(float angle, float x, float y, float z, float px, float py, float pz)
+{
+ m[12] -= px;
+ m[13] -= py;
+ m[14] -= pz;
+
+ Rotate(angle, x, y, z);
+
+ m[12] += px;
+ m[13] += py;
+ m[14] += pz;
+}
+
+void Matrix::Translate(float x, float y, float z)
+{
+ m[12] = m[0] * x + m[4] * y + m[8] * z + m[12];
+ m[13] = m[1] * x + m[5] * y + m[9] * z + m[13];
+ m[14] = m[2] * x + m[6] * y + m[10] * z + m[14];
+ m[15] = m[3] * x + m[7] * y + m[11] * z + m[15];
+}
+
+void Matrix::SetTranslation(float x, float y, float z)
+{
+ m[12] = x;
+ m[13] = y;
+ m[14] = z;
+ m[15] = 1;
+}
+
+void Matrix::GetTranslation(float* x, float* y, float* z)
+{
+ *x = m[12];
+ *y = m[13];
+ *z = m[14];
+}
+
+void Matrix::GetTranslation(float pos[3])
+{
+ pos[0] = m[12];
+ pos[1] = m[13];
+ pos[2] = m[14];
+}
+
+void Matrix::SetTranslation(float pos[3])
+{
+ m[12] = pos[0];
+ m[13] = pos[1];
+ m[14] = pos[2];
+ m[15] = 1;
+}
+
+void Matrix::Create(float mx, float my, float mz, float rx, float ry, float rz)
+{
+ LoadIdentity();
+ Translate(mx, my, mz);
+ Rotate(rx, 1, 0, 0);
+ Rotate(ry, 0, 1, 0);
+ Rotate(rz, 0, 0, 1);
+}
+
+void Matrix::CreateOld(float mx, float my, float mz, float rx, float ry, float rz)
+{
+ LoadIdentity();
+ Translate(mx, my, mz);
+
+ float rm[16];
+ rotation_matrix(rx, 1, 0, 0, rm);
+ matmul(m, m, rm);
+ rotation_matrix(ry, 0, 1, 0, rm);
+ matmul(m, m, rm);
+ rotation_matrix(rz, 0, 0, 1, rm);
+ matmul(m, m, rm);
+}
+
+// Transform a point by a 4x4 matrix. out = m * in
+void Matrix::TransformPoint(float out[], const float in[3])
+{
+ out[0] = m[0]*in[0] + m[4]*in[1] + m[8]*in[2] + m[12];
+ out[1] = m[1]*in[0] + m[5]*in[1] + m[9]*in[2] + m[13];
+ out[2] = m[2]*in[0] + m[6]*in[1] + m[10]*in[2] + m[14];
+}
+
+void Matrix::TransformPoints (float p[], int n)
+{
+ for (int i = 0; i < n*3; i += 3)
+ {
+ float tmp[3] = { p[i], p[i+1], p[i+2] };
+ TransformPoint (&p[i], tmp);
+ }
+}
+
+void Matrix::ConvertFromLDraw(float f[12])
+{
+ float trans[16] = { 1,0,0,0, 0,0,-1,0, 0,1,0,0, 0,0,0,1 };
+ float t[16] = { 1,0,0,0, 0,0,1,0, 0,-1,0,0, 0,0,0,1 };
+ m[0] = f[3]; m[1] = f[6]; m[2] = f[9];
+ m[4] = f[4]; m[5] = f[7]; m[6] = f[10];
+ m[8] = f[5]; m[9] = f[8]; m[10]= f[11];
+ m[12]= f[0]/25; m[13]= f[1]/25; m[14]= f[2]/25;
+ matmul (m, m, t);
+ matmul (trans, trans, m);
+ memcpy (&m[0], &trans[0], sizeof(m));
+}
+
+void Matrix::ConvertToLDraw(float f[12])
+{
+ float trans[16] = { 1,0,0,0, 0,0,-1,0, 0,1,0,0, 0,0,0,1 };
+ float tmp[16] = { 1,0,0,0, 0,0,1,0, 0,-1,0,0, 0,0,0,1 };
+ matmul(tmp, tmp, m);
+ matmul (tmp, tmp, trans);
+ f[0] = m[12]*25; f[1] = -m[14]*25; f[2] = m[13]*25;
+ f[3] = tmp[0]; f[4] = tmp[4]; f[5] = tmp[8];
+ f[6] = tmp[1]; f[7] = tmp[5]; f[8] = tmp[9];
+ f[9] = tmp[2]; f[10]= tmp[6]; f[11]= tmp[10];
+}
+
+void Matrix::ReadFromFile(File* F)
+{
+ float tmp[12];
+ F->Read (&tmp, sizeof(tmp));
+ m[0] = tmp[0]; m[1] = tmp[1]; m[2] = tmp[2];
+ m[4] = tmp[3]; m[5] = tmp[4]; m[6] = tmp[5];
+ m[8] = tmp[6]; m[9] = tmp[7]; m[10]= tmp[8];
+ m[12]= tmp[9]; m[13]= tmp[10]; m[14]= tmp[11];
+}
+
+void Matrix::WriteToFile(File* F)
+{
+ float tmp[12];
+ tmp[0] = m[0]; tmp[1] = m[1]; tmp[2] = m[2];
+ tmp[3] = m[4]; tmp[4] = m[5]; tmp[5] = m[6];
+ tmp[6] = m[8]; tmp[7] = m[9]; tmp[8] = m[10];
+ tmp[9] = m[12]; tmp[10]= m[13]; tmp[11]= m[14];
+ F->Write (&tmp, sizeof(tmp));
+}
+
+void Matrix::GetEulerAngles(float rot[3])
+{
+ double sinPitch, cosPitch, sinRoll, cosRoll, sinYaw, cosYaw;
+ float colMatrix[4][4];
+
+ colMatrix[0][0] = m[0];
+ colMatrix[0][1] = m[4];
+ colMatrix[0][2] = m[8];
+ colMatrix[0][3] = m[12];
+
+ colMatrix[1][0] = m[1];
+ colMatrix[1][1] = m[5];
+ colMatrix[1][2] = m[9];
+ colMatrix[1][3] = m[13];
+
+ colMatrix[2][0] = m[2];
+ colMatrix[2][1] = m[6];
+ colMatrix[2][2] = m[10];
+ colMatrix[2][3] = m[14];
+
+ colMatrix[3][0] = 0.0f;
+ colMatrix[3][1] = 0.0f;
+ colMatrix[3][2] = 0.0f;
+ colMatrix[3][3] = 1.0f;
+
+ sinPitch = -colMatrix[2][0];
+ cosPitch = sqrt(1 - sinPitch*sinPitch);
+
+ if (fabs(cosPitch) > 0.0005)
+ {
+ sinRoll = colMatrix[2][1] / cosPitch;
+ cosRoll = colMatrix[2][2] / cosPitch;
+ sinYaw = colMatrix[1][0] / cosPitch;
+ cosYaw = colMatrix[0][0] / cosPitch;
+ }
+ else
+ {
+ sinRoll = -colMatrix[1][2];
+ cosRoll = colMatrix[1][1];
+ sinYaw = 0;
+ cosYaw = 1;
+ }
+
+ rot[2] = (float)(RTOD*atan2 (sinYaw, cosYaw));
+ rot[1] = (float)(RTOD*atan2 (sinPitch, cosPitch));
+ rot[0] = (float)(RTOD*atan2 (sinRoll, cosRoll));
+
+ if (rot[2] < 0) rot[2] += 360;
+ if (rot[1] < 0) rot[1] += 360;
+ if (rot[0] < 0) rot[0] += 360;
+}
+
+
+void Matrix::ToAxisAngle(float rot[4])
+{
+ double matrix[3][4];
+ double q[4];
+ matrix[0][0] = m[0];
+ matrix[0][1] = m[4];
+ matrix[0][2] = m[8];
+ matrix[0][3] = m[12];
+
+ matrix[1][0] = m[1];
+ matrix[1][1] = m[5];
+ matrix[1][2] = m[9];
+ matrix[1][3] = m[13];
+
+ matrix[2][0] = m[2];
+ matrix[2][1] = m[6];
+ matrix[2][2] = m[10];
+ matrix[2][3] = m[14];
+
+ double trace, s;
+ int i, j, k;
+ static int next[3] = {1, 2, 0};
+
+ trace = matrix[0][0] + matrix[1][1] + matrix[2][2];
+
+ if (trace > 0.0)
+ {
+ s = sqrt(trace + 1.0);
+ q[3] = s * 0.5;
+ s = 0.5 / s;
+
+ q[0] = (matrix[2][1] - matrix[1][2]) * s;
+ q[1] = (matrix[0][2] - matrix[2][0]) * s;
+ q[2] = (matrix[1][0] - matrix[0][1]) * s;
+ }
+ else
+ {
+ i = 0;
+ if (matrix[1][1] > matrix[0][0])
+ i = 1;
+ if (matrix[2][2] > matrix[i][i])
+ i = 2;
+
+ j = next[i];
+ k = next[j];
+
+ s = sqrt( (matrix[i][i] - (matrix[j][j]+matrix[k][k])) + 1.0 );
+
+ q[i] = s * 0.5;
+
+ s = 0.5 / s;
+
+ q[3] = (matrix[k][j] - matrix[j][k]) * s;
+ q[j] = (matrix[j][i] + matrix[i][j]) * s;
+ q[k] = (matrix[k][i] + matrix[i][k]) * s;
+ }
+
+ double cos_angle = q[3];
+ rot[3] = (float)acos(cos_angle) * 2 * RTOD;
+ double sin_angle = sqrt( 1.0 - cos_angle * cos_angle );
+ if (fabs(sin_angle) < 1E-10)
+ sin_angle = 1;
+
+ rot[0] = (float)(q[0] / sin_angle);
+ rot[1] = (float)(q[1] / sin_angle);
+ rot[2] = (float)(q[2] / sin_angle);
+
+ if (fabs(rot[3]) < 1E-10)
+ {
+ rot[0] = rot[1] = rot[3] = 0;
+ rot[2] = 1;
+ }
+}
+
+void Matrix::FromEuler(float roll, float pitch, float yaw)
+{
+ float cosYaw, sinYaw, cosPitch, sinPitch, cosRoll, sinRoll;
+
+ cosYaw = (float)cos(yaw*DTOR);
+ sinYaw = (float)sin(yaw*DTOR);
+ cosPitch = (float)cos(pitch*DTOR);
+ sinPitch = (float)sin(pitch*DTOR);
+ cosRoll = (float)cos(roll*DTOR);
+ sinRoll = (float)sin(roll*DTOR);
+
+ m[0] = cosYaw * cosPitch;
+ m[4] = cosYaw * sinPitch * sinRoll - sinYaw * cosRoll;
+ m[8] = cosYaw * sinPitch * cosRoll + sinYaw * sinRoll;
+ m[12] = 0.0f;
+
+ m[1] = sinYaw * cosPitch;
+ m[5] = cosYaw * cosRoll + sinYaw * sinPitch * sinRoll;
+ m[9] = sinYaw * sinPitch * cosRoll - cosYaw * sinRoll;
+ m[13] = 0.0f;
+
+ m[2] = -sinPitch;
+ m[6] = cosPitch * sinRoll;
+ m[10] = cosPitch * cosRoll;
+ m[14] = 0.0f;
+
+ m[3] = 0.0f;
+ m[7] = 0.0f;
+ m[11] = 0.0f;
+ m[15] = 1.0f;
+}
+
+// Create a rotation matrix (angle is in degrees)
+void Matrix::FromAxisAngle(float axis[3], float angle)
+{
+ if (angle == 0.0f)
+ return;
+ rotation_matrix(angle, axis[0], axis[1], axis[2], m);
+}
+
+bool Matrix::FromInverse(double* src)
+{
+/*
+// This code is better !
+ float det_1, pos, neg, temp;
+
+#define ACCUMULATE \
+ if (temp >= 0.0) \
+ pos += temp; \
+ else \
+ neg += temp;
+
+#define PRECISION_LIMIT (1.0e-15)
+
+ // Calculate the determinant of submatrix A and determine if the
+ // the matrix is singular as limited by the double precision
+ // floating-point data representation.
+ pos = neg = 0.0f;
+ temp = src[0] * src[5] * src[10];
+ ACCUMULATE
+ temp = src[1] * src[6] * src[8];
+ ACCUMULATE
+ temp = src[2] * src[4] * src[9];
+ ACCUMULATE
+ temp = -src[2] * src[5] * src[8];
+ ACCUMULATE
+ temp = -src[1] * src[4] * src[10];
+ ACCUMULATE
+ temp = -src[0] * src[6] * src[9];
+ ACCUMULATE
+ det_1 = pos + neg;
+#define ABS(a) (a < 0) ? -a : a
+ // Is the submatrix A singular ?
+ if ((det_1 == 0.0) || (ABS(det_1 / (pos - neg)) < PRECISION_LIMIT))
+ return false;
+
+ // Calculate inverse(A) = adj(A) / det(A)
+ det_1 = 1.0f / det_1;
+ inverse[0] = (src[5]*src[10] - src[6]*src[9])*det_1;
+ inverse[4] = -(src[4]*src[10] - src[6]*src[8])*det_1;
+ inverse[8] = (src[4]*src[9] - src[5]*src[8])*det_1;
+ inverse[1] = -(src[1]*src[10] - src[2]*src[9])*det_1;
+ inverse[5] = (src[0]*src[10] - src[2]*src[8])*det_1;
+ inverse[9] = -(src[0]*src[9] - src[1]*src[8])*det_1;
+ inverse[2] = (src[1]*src[6] - src[2]*src[5])*det_1;
+ inverse[6] = -(src[0]*src[6] - src[2]*src[4])*det_1;
+ inverse[10] = (src[0]*src[5] - src[1]*src[4])*det_1;
+
+ // Calculate -C * inverse(A)
+ inverse[12] = -(src[12]*inverse[0] + src[13]*inverse[4] + src[14]*inverse[8]);
+ inverse[13] = -(src[12]*inverse[1] + src[13]*inverse[5] + src[14]*inverse[9]);
+ inverse[14] = -(src[12]*inverse[2] + src[13]*inverse[6] + src[14]*inverse[10]);
+
+ // Fill in last column
+ inverse[3] = inverse[7] = inverse[11] = 0.0f;
+ inverse[15] = 1.0f;
+
+ return true;
+*/
+ float t;
+ int i, j, k, swap;
+ float tmp[4][4];
+
+ for (i = 0; i < 16; i++)
+ m[i] = 0.0;
+ m[0] = m[5] = m[10] = m[15] = 1.0;
+
+ for (i = 0; i < 4; i++)
+ for (j = 0; j < 4; j++)
+ tmp[i][j] = (float)src[i*4+j];
+
+ for (i = 0; i < 4; i++)
+ {
+ // look for largest element in column.
+ swap = i;
+ for (j = i + 1; j < 4; j++)
+ if (fabs(tmp[j][i]) > fabs(tmp[i][i]))
+ swap = j;
+
+ if (swap != i)
+ {
+ // swap rows.
+ for (k = 0; k < 4; k++)
+ {
+ t = tmp[i][k];
+ tmp[i][k] = tmp[swap][k];
+ tmp[swap][k] = t;
+
+ t = m[i*4+k];
+ m[i*4+k] = m[swap*4+k];
+ m[swap*4+k] = t;
+ }
+ }
+
+ if (tmp[i][i] == 0)
+ {
+ // The matrix is singular, which shouldn't happen.
+ return false;
+ }
+
+ t = tmp[i][i];
+ for (k = 0; k < 4; k++)
+ {
+ tmp[i][k] /= t;
+ m[i*4+k] /= t;
+ }
+ for (j = 0; j < 4; j++)
+ {
+ if (j != i)
+ {
+ t = tmp[j][i];
+ for (k = 0; k < 4; k++)
+ {
+ tmp[j][k] -= tmp[i][k]*t;
+ m[j*4+k] -= m[i*4+k]*t;
+ }
+ }
+ }
+ }
+
+ return true;
+} \ No newline at end of file
diff --git a/common/matrix.h b/common/matrix.h
new file mode 100644
index 0000000..41110d9
--- /dev/null
+++ b/common/matrix.h
@@ -0,0 +1,45 @@
+// Matrix class
+//
+
+#ifndef _MATRIX_H_
+#define _MATRIX_H_
+
+class File;
+
+class Matrix
+{
+public:
+ Matrix();
+ Matrix(float* floats);
+ Matrix(double mat[16]);
+ Matrix(float rot[4], float pos[3]);
+ ~Matrix();
+
+ void WriteToFile (File* F);
+ void ReadFromFile (File* F);
+ void Multiply (Matrix& m1, Matrix& m2);
+ void ConvertToLDraw(float f[12]);
+ void ConvertFromLDraw(float f[12]);
+ void GetEulerAngles (float rot[3]);
+ void LoadIdentity();
+ void GetTranslation(float *x, float *y, float *z);
+ void SetTranslation(float x, float y, float z);
+ void GetTranslation(float pos[3]);
+ void SetTranslation(float pos[3]);
+ void TransformPoint(float out[], const float in[3]);
+ void TransformPoints (float p[], int n);
+ void Create (float mx, float my, float mz, float rx, float ry, float rz);
+ void CreateOld(float mx, float my, float mz, float rx, float ry, float rz);
+ void Rotate(float angle, float x, float y, float z);
+ void RotateCenter(float angle, float x, float y, float z, float px, float py, float pz);
+ void Translate(float x, float y, float z);
+ void FromEuler(float yaw, float pitch, float roll);
+ void ToAxisAngle(float rot[4]);
+ void FromAxisAngle(float axis[3], float angle);
+ void FromFloat(float* mat);
+ bool FromInverse(double* src);
+
+ float m[16];
+};
+
+#endif //_MATRIX_H_
diff --git a/common/module.mk b/common/module.mk
new file mode 100644
index 0000000..aead809
--- /dev/null
+++ b/common/module.mk
@@ -0,0 +1,3 @@
+SRC += common/boundbox.cpp common/camera.cpp common/file.cpp common/globals.cpp common/group.cpp common/image.cpp common/light.cpp common/matrix.cpp common/piece.cpp common/pieceinf.cpp common/project.cpp common/quant.cpp common/terrain.cpp common/texture.cpp common/tr.cpp common/vector.cpp
+
+LIBS += -lGLU -lGL -ljpeg
diff --git a/common/piece.cpp b/common/piece.cpp
new file mode 100644
index 0000000..99fcef9
--- /dev/null
+++ b/common/piece.cpp
@@ -0,0 +1,1771 @@
+// A piece object in the LeoCAD world.
+//
+
+#ifdef _WINDOWS
+#include "stdafx.h"
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include "matrix.h"
+#include "pieceinf.h"
+#include "boundbox.h"
+#include "texture.h"
+#include "piece.h"
+#include "group.h"
+#include "project.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// Static functions
+
+static PIECE_KEY* AddNode (PIECE_KEY *node, unsigned short nTime, unsigned char nType)
+{
+ PIECE_KEY* newnode = (PIECE_KEY*)malloc(sizeof(PIECE_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;
+
+ if (nType == PK_ROTATION)
+ newnode->param[2] = 1;
+
+ return newnode;
+}
+
+inline static void SetCurrentColor(unsigned char nColor, bool* bTrans, bool bLighting, bool bNoAlpha)
+{
+ if (bLighting || !bNoAlpha)
+ glColor4fv(ColorArray[nColor]);
+ else
+ glColor3fv(FlatColorArray[nColor]);
+
+ if (nColor > 27)
+ return;
+
+ if (bNoAlpha)
+ {
+ if (nColor > 13 && nColor < 22)
+ {
+ if (!*bTrans)
+ {
+ *bTrans = true;
+ glEnable(GL_POLYGON_STIPPLE);
+ }
+ }
+ else
+ {
+ if (*bTrans)
+ {
+ *bTrans = false;
+ glDisable(GL_POLYGON_STIPPLE);
+ }
+ }
+ }
+ else
+ {
+ if (nColor > 13 && nColor < 22)
+ {
+ if (!*bTrans)
+ {
+ *bTrans = true;
+ glEnable(GL_BLEND);
+ glDepthMask(GL_FALSE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ }
+ else
+ {
+ if (*bTrans)
+ {
+ *bTrans = false;
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+ }
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Piece construction/destruction
+
+static bool lockarrays = false;
+
+#ifdef _WINDOWS
+typedef void (APIENTRY * GLLOCKARRAYSEXTPROC) (GLint first, GLsizei count);
+typedef void (APIENTRY * GLUNLOCKARRAYSEXTPROC) ();
+static GLLOCKARRAYSEXTPROC glLockArraysEXT = NULL;
+static GLUNLOCKARRAYSEXTPROC glUnlockArraysEXT = NULL;
+#else
+#define glLockArraysEXT(a,b) {}
+#define glUnlockArraysEXT() {}
+#endif
+
+Piece::Piece(PieceInfo* pPieceInfo)
+{
+#ifdef _WINDOWS
+ static bool first_time = true;
+
+ if (first_time)
+ {
+ first_time = false;
+ char* extensions = (char*)glGetString(GL_EXTENSIONS);
+
+ if (strstr(extensions, "GL_EXT_compiled_vertex_array "))
+ {
+ glLockArraysEXT = (GLLOCKARRAYSEXTPROC)wglGetProcAddress("glLockArraysEXT");
+ glUnlockArraysEXT = (GLUNLOCKARRAYSEXTPROC)wglGetProcAddress("glUnlockArraysEXT");
+ lockarrays = true;
+ }
+ }
+#endif
+
+ m_BoundingBox.Initialize(this, LC_PIECE);
+ m_pNext = NULL;
+ m_pPieceInfo = pPieceInfo;
+ m_nState = 0;
+ m_nColor = 0;
+ m_pInstructionKeys = NULL;
+ m_pAnimationKeys = NULL;
+ m_nStepShow = 1;
+ m_nStepHide = 255;
+ m_nFrameHide = 65535;
+ memset(m_strName, 0, sizeof(m_strName));
+ m_pGroup = NULL;
+ m_pDrawInfo = NULL;
+ m_pConnections = NULL;
+
+ if (m_pPieceInfo != NULL)
+ {
+ m_nBoxList = m_pPieceInfo->AddRef();
+ if (m_pPieceInfo->m_nConnectionCount > 0)
+ {
+ m_pConnections = (CONNECTION*)malloc(sizeof(CONNECTION)*(m_pPieceInfo->m_nConnectionCount));
+
+ for (int i = 0; i < m_pPieceInfo->m_nConnectionCount; i++)
+ {
+ m_pConnections[i].type = m_pPieceInfo->m_pConnections[i].type;
+ m_pConnections[i].link = NULL;
+ m_pConnections[i].owner = this;
+ }
+ }
+ }
+}
+
+Piece::~Piece()
+{
+ RemoveKeys();
+ if (m_pPieceInfo != NULL)
+ m_pPieceInfo->DeRef();
+ if (m_pDrawInfo != NULL)
+ free(m_pDrawInfo);
+ if (m_pConnections != NULL)
+ free(m_pConnections);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Piece save/load
+
+// Use only when loading from a file
+void Piece::SetPieceInfo(PieceInfo* pPieceInfo)
+{
+ m_pPieceInfo = pPieceInfo;
+ m_nBoxList = m_pPieceInfo->AddRef();
+
+ if (m_pPieceInfo->m_nConnectionCount > 0)
+ {
+ m_pConnections = (CONNECTION*)malloc(sizeof(CONNECTION)*(m_pPieceInfo->m_nConnectionCount));
+
+ for (int i = 0; i < m_pPieceInfo->m_nConnectionCount; i++)
+ {
+ m_pConnections[i].type = m_pPieceInfo->m_pConnections[i].type;
+ m_pConnections[i].link = NULL;
+ m_pConnections[i].owner = this;
+ }
+ }
+}
+
+void Piece::FileLoad(File* file, char* name)
+{
+ PIECE_KEY *node;
+ unsigned char version, ch;
+
+ file->Read(&version, 1);
+
+ if (version > 5)
+ {
+ unsigned long keys;
+
+ file->Read(&keys, 4);
+ for (node = NULL; keys--;)
+ {
+ if (node == NULL)
+ {
+ m_pInstructionKeys = AddNode(NULL, 1, PK_POSITION);
+ node = m_pInstructionKeys;
+ }
+ else
+ node = AddNode(node, 1, PK_POSITION);
+
+ file->Read(node->param, 16);
+ file->Read(&node->time, 2);
+ file->Read(&node->type, 1);
+ }
+
+ file->Read(&keys, 4);
+ for (node = NULL; keys--;)
+ {
+ if (node == NULL)
+ {
+ m_pAnimationKeys = AddNode(NULL, 1, PK_POSITION);
+ node = m_pAnimationKeys;
+ }
+ else
+ node = AddNode(node, 1, PK_POSITION);
+
+ file->Read(node->param, 16);
+ file->Read(&node->time, 2);
+ file->Read(&node->type, 1);
+ }
+ }
+ else
+ {
+ if (version > 2)
+ {
+ file->Read(&ch, 1);
+
+ for (node = NULL; ch--;)
+ {
+ if (node == NULL)
+ {
+ m_pInstructionKeys = AddNode(NULL, 1, PK_POSITION);
+ node = m_pInstructionKeys;
+ }
+ else
+ node = AddNode(node, 1, PK_POSITION);
+
+ Matrix mat;
+ if (version > 3)
+ {
+ float m[16];
+ file->Read(m, sizeof(m));
+ mat.FromFloat(m);
+ }
+ else
+ {
+ float move[3], rotate[3];
+ file->Read(move, sizeof(float[3]));
+ file->Read(rotate, sizeof(float[3]));
+ mat.CreateOld(move[0], move[1], move[2], rotate[0], rotate[1], rotate[2]);
+ }
+
+ unsigned char b;
+ file->Read(&b, 1);
+ node->time = b;
+ mat.GetTranslation(&node->param[0], &node->param[1], &node->param[2]);
+ node = AddNode(node, b, PK_ROTATION);
+ mat.ToAxisAngle(node->param);
+
+ int bl;
+ file->Read(&bl, 4);
+ }
+ }
+ else
+ {
+ Matrix mat;
+ float move[3], rotate[3];
+ file->Read(move, sizeof(float[3]));
+ file->Read(rotate, sizeof(float[3]));
+ mat.CreateOld(move[0], move[1], move[2], rotate[0], rotate[1], rotate[2]);
+
+ m_pInstructionKeys = AddNode (NULL, 1, PK_POSITION);
+ mat.GetTranslation(&m_pInstructionKeys->param[0], &m_pInstructionKeys->param[1], &m_pInstructionKeys->param[2]);
+ AddNode(m_pInstructionKeys, 1, PK_ROTATION);
+ mat.ToAxisAngle(m_pInstructionKeys->next->param);
+ }
+
+ m_pAnimationKeys = AddNode (NULL, 1, PK_POSITION);
+ AddNode(m_pAnimationKeys, 1, PK_ROTATION);
+ memcpy(m_pAnimationKeys->param, m_pInstructionKeys->param, sizeof(float[4]));
+ memcpy(m_pAnimationKeys->next->param, m_pInstructionKeys->next->param, sizeof(float[4]));
+ }
+
+ // Common to all versions.
+ file->Read(name, 9);
+ file->Read(&m_nColor, 1);
+
+ if (version < 5)
+ {
+ const unsigned char conv[20] = { 0,2,4,9,7,6,22,8,10,11,14,16,18,9,21,20,22,8,10,11 };
+ m_nColor = conv[m_nColor];
+ }
+
+ file->Read(&m_nStepShow, 1);
+ if (version > 1)
+ file->Read(&m_nStepHide, 1);
+ else
+ m_nStepHide = 255;
+
+ if (version > 5)
+ {
+ file->Read(&m_nFrameShow, 2);
+ file->Read(&m_nFrameHide, 2);
+
+ if (version > 7)
+ {
+ file->Read(&m_nState, 1);
+ UnSelect();
+ file->Read(&ch, 1);
+ file->Read(m_strName, ch);
+ }
+ else
+ {
+ int hide;
+ file->Read(&hide, 4);
+ if (hide != 0)
+ m_nState |= LC_PIECE_HIDDEN;
+ file->Read(m_strName, 81);
+ }
+
+ // 7 (0.64)
+ int i = -1;
+ if (version > 6)
+ file->Read(&i, 4);
+ m_pGroup = (Group*)i;
+ }
+ else
+ {
+ m_nFrameShow = 1;
+ m_nFrameHide = 65535;
+
+ file->Read(&ch, 1);
+ if (ch == 0)
+ m_pGroup = (Group*)-1;
+ else
+ m_pGroup = (Group*)ch;
+
+ file->Read(&ch, 1);
+ if (ch & 0x01)
+ m_nState |= LC_PIECE_HIDDEN;
+ }
+}
+
+void Piece::FileSave(File* file, Group* pGroups)
+{
+ PIECE_KEY *node;
+ unsigned char ch = 8; // LeoCAD 0.70
+ unsigned long n;
+
+ file->Write(&ch, 1);
+
+ for (n = 0, node = m_pInstructionKeys; node; node = node->next)
+ n++;
+ file->Write(&n, 4);
+
+ for (node = m_pInstructionKeys; node; node = node->next)
+ {
+ file->Write(node->param, 16);
+ file->Write(&node->time, 2);
+ file->Write(&node->type, 1);
+ }
+
+ for (n = 0, node = m_pAnimationKeys; node; node = node->next)
+ n++;
+ file->Write(&n, 4);
+
+ for (node = m_pAnimationKeys; node; node = node->next)
+ {
+ file->Write(node->param, 16);
+ file->Write(&node->time, 2);
+ file->Write(&node->type, 1);
+ }
+
+ file->Write(m_pPieceInfo->m_strName, 9);
+
+ file->Write(&m_nColor, 1);
+ file->Write(&m_nStepShow, 1);
+ file->Write(&m_nStepHide, 1);
+ file->Write(&m_nFrameShow, 2);
+ file->Write(&m_nFrameHide, 2);
+
+ // version 8
+ file->Write(&m_nState, 1);
+ ch = strlen(m_strName);
+ file->Write(&ch, 1);
+ file->Write(m_strName, ch);
+
+ // version 7
+ int i;
+ if (m_pGroup != NULL)
+ {
+ for (i = 0; pGroups; pGroups = pGroups->m_pNext)
+ {
+ if (m_pGroup == pGroups)
+ break;
+ i++;
+ }
+ }
+ else
+ i = -1;
+ file->Write(&i, 4);
+}
+
+void Piece::Initialize(float x, float y, float z, unsigned char nStep, unsigned short nFrame, unsigned char nColor)
+{
+ m_nFrameShow = nFrame;
+ m_nStepShow = nStep;
+
+ m_pAnimationKeys = AddNode (NULL, 1, PK_POSITION);
+ AddNode(m_pAnimationKeys, 1, PK_ROTATION);
+ m_pAnimationKeys->param[0] = x;
+ m_pAnimationKeys->param[1] = y;
+ m_pAnimationKeys->param[2] = z;
+
+ m_pInstructionKeys = AddNode (NULL, 1, PK_POSITION);
+ AddNode(m_pInstructionKeys, 1, PK_ROTATION);
+ m_pInstructionKeys->param[0] = x;
+ m_pInstructionKeys->param[1] = y;
+ m_pInstructionKeys->param[2] = z;
+
+ UpdatePosition(1, false);
+
+ m_nColor = nColor;
+}
+
+void Piece::CreateName(Piece* pPiece)
+{
+ int i, max = 0;
+
+ for (; pPiece; pPiece = pPiece->m_pNext)
+ if (strncmp (pPiece->m_strName, m_pPieceInfo->m_strDescription, strlen(m_pPieceInfo->m_strDescription)) == 0)
+ if (sscanf(pPiece->m_strName + strlen(m_pPieceInfo->m_strDescription), " #%d", &i) == 1)
+ if (i > max)
+ max = i;
+
+ sprintf (m_strName, "%s #%0.2d", m_pPieceInfo->m_strDescription, max+1);
+}
+
+void Piece::LineFacet(float* p1, float* p2, float* p3, float* p4, CLICKLINE* pLine)
+{
+ double t, t1, t2, x, y, z, plane[4];
+ plane[0] = ((p1[1]-p2[1])*(p3[2]-p2[2])) - ((p1[2]-p2[2])*(p3[1]-p2[1]));
+ plane[1] = ((p1[2]-p2[2])*(p3[0]-p2[0])) - ((p1[0]-p2[0])*(p3[2]-p2[2]));
+ plane[2] = ((p1[0]-p2[0])*(p3[1]-p2[1])) - ((p1[1]-p2[1])*(p3[0]-p2[0]));
+ plane[3] = -(plane[0]*p1[0]) -(plane[1]*p1[1]) -(plane[2]*p1[2]);
+ t1 = (plane[0]*pLine->a1 + plane[1]*pLine->b1 + plane[2]*pLine->c1 + plane[3]);
+ t2 = (plane[0]*pLine->a2 + plane[1]*pLine->b2 + plane[2]*pLine->c2);
+
+ if (t1 != 0 && t2 != 0)
+ {
+ t = -(t1 / t2);
+ if (t >= 0)
+ {
+ x = pLine->a1+pLine->a2*t;
+ y = pLine->b1+pLine->b2*t;
+ z = pLine->c1+pLine->c2*t;
+
+ if (fabs(plane[0]*x + plane[1]*y + plane[2]*z + plane[3]) <= 0.001)
+ {
+ double dist = sqrt((pLine->a1-x)*(pLine->a1-x)+(pLine->b1-y)*(pLine->b1-y)+(pLine->c1-z)*(pLine->c1-z));
+
+ if (dist < pLine->mindist)
+ {
+ double pa1[3], pa2[3], pa3[3], a1, a2, a3, inv;
+ pa1[0] = p1[0] - x;
+ pa1[1] = p1[1] - y;
+ pa1[2] = p1[2] - z;
+ inv = 1.0/sqrt(pa1[0]*pa1[0] + pa1[1]*pa1[1] + pa1[2]*pa1[2]);
+ pa1[0] *= inv;
+ pa1[1] *= inv;
+ pa1[2] *= inv;
+ pa2[0] = p2[0] - x;
+ pa2[1] = p2[1] - y;
+ pa2[2] = p2[2] - z;
+ inv = 1.0/sqrt(pa2[0]*pa2[0] + pa2[1]*pa2[1] + pa2[2]*pa2[2]);
+ pa2[0] *= inv;
+ pa2[1] *= inv;
+ pa2[2] *= inv;
+ pa3[0] = p3[0] - x;
+ pa3[1] = p3[1] - y;
+ pa3[2] = p3[2] - z;
+ inv = 1.0/sqrt(pa3[0]*pa3[0] + pa3[1]*pa3[1] + pa3[2]*pa3[2]);
+ pa3[0] *= inv;
+ pa3[1] *= inv;
+ pa3[2] *= inv;
+ a1 = pa1[0]*pa2[0] + pa1[1]*pa2[1] + pa1[2]*pa2[2];
+ a2 = pa2[0]*pa3[0] + pa2[1]*pa3[1] + pa2[2]*pa3[2];
+ a3 = pa3[0]*pa1[0] + pa3[1]*pa1[1] + pa3[2]*pa1[2];
+ double total = (acos(a1) + acos(a2) + acos(a3)) * RTOD;
+
+ if (fabs(total - 360) > 0.001) // Outside triangle
+ {
+ if (p4 != NULL)
+ {
+ pa2[0] = p4[0] - x;
+ pa2[1] = p4[1] - y;
+ pa2[2] = p4[2] - z;
+ inv = 1.0/sqrt(pa2[0]*pa2[0] + pa2[1]*pa2[1] + pa2[2]*pa2[2]);
+ pa2[0] *= inv;
+ pa2[1] *= inv;
+ pa2[2] *= inv;
+ a1 = pa1[0]*pa2[0] + pa1[1]*pa2[1] + pa1[2]*pa2[2];
+ a2 = pa2[0]*pa3[0] + pa2[1]*pa3[1] + pa2[2]*pa3[2];
+ a3 = pa3[0]*pa1[0] + pa3[1]*pa1[1] + pa3[2]*pa1[2];
+ total = (acos(a1) + acos(a2) + acos(a3)) * RTOD;
+
+ if (fabs(total - 360) > 0.001)
+ return; // Outside other triangle
+ }
+ else
+ return;
+ }
+
+ pLine->mindist = dist;
+ pLine->pClosest = &m_BoundingBox;
+ }
+ }
+ }
+ }
+}
+
+void Piece::MinIntersectDist(CLICKLINE* pLine)
+{
+ double dist;
+
+ dist = m_BoundingBox.FindIntersectDist(pLine);
+ if (dist >= pLine->mindist)
+ return;
+
+ Matrix mat(m_fRotation, m_fPosition);
+ float* verts = (float*)malloc(sizeof(float)*3*m_pPieceInfo->m_nVertexCount);
+ memcpy(verts, m_pPieceInfo->m_fVertexArray, sizeof(float)*3*m_pPieceInfo->m_nVertexCount);
+ mat.TransformPoints(verts, m_pPieceInfo->m_nVertexCount);
+
+ if (m_pPieceInfo->m_nFlags & LC_PIECE_LONGDATA)
+ {
+ unsigned long* info = (unsigned long*)m_pDrawInfo, colors, i;
+ colors = *info;
+ info++;
+
+ while (colors--)
+ {
+ info++;
+ for (i = 0; i < *info; i += 4)
+ LineFacet(&verts[info[i+1]*3], &verts[info[i+2]*3],
+ &verts[info[i+3]*3], &verts[info[i+4]*3], pLine);
+ info += *info + 1;
+ for (i = 0; i < *info; i += 3)
+ LineFacet(&verts[info[i+1]*3], &verts[info[i+2]*3],
+ &verts[info[i+3]*3], NULL, pLine);
+ info += *info + 1;
+ info += *info + 1;
+ }
+ }
+ else
+ {
+ unsigned short* info = (unsigned short*)m_pDrawInfo, colors, i;
+ colors = *info;
+ info++;
+
+ while (colors--)
+ {
+ info++;
+ for (i = 0; i < *info; i += 4)
+ LineFacet(&verts[info[i+1]*3], &verts[info[i+2]*3],
+ &verts[info[i+3]*3], &verts[info[i+4]*3], pLine);
+ info += *info + 1;
+ for (i = 0; i < *info; i += 3)
+ LineFacet(&verts[info[i+1]*3], &verts[info[i+2]*3],
+ &verts[info[i+3]*3], NULL, pLine);
+ info += *info + 1;
+ info += *info + 1;
+ }
+ }
+
+ free(verts);
+}
+
+void Piece::Move(unsigned short nTime, bool bAnimation, bool bAddKey, float x, float y, float z)
+{
+ m_fPosition[0] += x;
+ m_fPosition[1] += y;
+ m_fPosition[2] += z;
+
+ ChangeKey(nTime, bAnimation, bAddKey, m_fPosition, PK_POSITION);
+}
+
+void Piece::ChangeKey(unsigned short nTime, bool bAnimation, bool bAddKey, float* param, unsigned char nKeyType)
+{
+ PIECE_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;
+
+ newpos->param[0] = param[0];
+ newpos->param[1] = param[1];
+ newpos->param[2] = param[2];
+
+ if (nKeyType == PK_ROTATION)
+ newpos->param[3] = param[3];
+}
+
+void Piece::RemoveKeys()
+{
+ PIECE_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);
+ }
+}
+
+bool Piece::IsVisible(unsigned short nTime, bool bAnimation)
+{
+ if (m_nState & LC_PIECE_HIDDEN)
+ return false;
+
+ if (bAnimation)
+ {
+ if (m_nFrameShow > nTime) return false;
+ if (m_nFrameHide < nTime) return false;
+ return true;
+ }
+ else
+ {
+ if (m_nStepShow > nTime) return false;
+ if ((m_nStepHide == 255) || (m_nStepHide > nTime))
+ return true;
+ return false;
+ }
+}
+
+void Piece::CompareBoundingBox(float box[6])
+{
+ float v[24] = {
+ m_pPieceInfo->m_fDimensions[0], m_pPieceInfo->m_fDimensions[1], m_pPieceInfo->m_fDimensions[5],
+ m_pPieceInfo->m_fDimensions[3], m_pPieceInfo->m_fDimensions[1], m_pPieceInfo->m_fDimensions[5],
+ m_pPieceInfo->m_fDimensions[0], m_pPieceInfo->m_fDimensions[1], m_pPieceInfo->m_fDimensions[2],
+ m_pPieceInfo->m_fDimensions[3], m_pPieceInfo->m_fDimensions[4], m_pPieceInfo->m_fDimensions[5],
+ m_pPieceInfo->m_fDimensions[3], m_pPieceInfo->m_fDimensions[4], m_pPieceInfo->m_fDimensions[2],
+ m_pPieceInfo->m_fDimensions[0], m_pPieceInfo->m_fDimensions[4], m_pPieceInfo->m_fDimensions[2],
+ m_pPieceInfo->m_fDimensions[0], m_pPieceInfo->m_fDimensions[4], m_pPieceInfo->m_fDimensions[5],
+ m_pPieceInfo->m_fDimensions[3], m_pPieceInfo->m_fDimensions[1], m_pPieceInfo->m_fDimensions[2] };
+
+ Matrix m(m_fRotation, m_fPosition);
+ m.TransformPoints(v, 8);
+
+ for (int i = 0; i < 24; i += 3)
+ {
+ if (v[i] < box[0]) box[0] = v[i];
+ if (v[i+1] < box[1]) box[1] = v[i+1];
+ if (v[i+2] < box[2]) box[2] = v[i+2];
+ if (v[i] > box[3]) box[3] = v[i];
+ if (v[i+1] > box[4]) box[4] = v[i+1];
+ if (v[i+2] > box[5]) box[5] = v[i+2];
+ }
+}
+
+bool Piece::CalculatePositionRotation(unsigned short nTime, bool bAnimation, float pos[3], float rot[4])
+{
+ PIECE_KEY *node, *prevpos = NULL, *nextpos = NULL, *prevrot = NULL, *nextrot = NULL;
+ if (bAnimation)
+ node = m_pAnimationKeys;
+ else
+ node = m_pInstructionKeys;
+
+ while (node && (!nextpos || !nextrot))
+ {
+ if (node->time <= nTime)
+ {
+ if (node->type == PK_POSITION)
+ prevpos = node;
+ else
+ prevrot = node;
+ }
+ else
+ {
+ if (node->type == PK_POSITION)
+ {
+ if (nextpos == NULL)
+ nextpos = node;
+ }
+ else
+ {
+ if (nextrot == NULL)
+ nextrot = node;
+ }
+ }
+
+ node = node->next;
+ }
+
+ if (bAnimation)
+ {
+ if ((nextpos != NULL) && (prevpos->time != nTime))
+ {
+ // TODO: USE KEY IN/OUT WEIGHTS
+ float t = (float)(nTime - prevpos->time)/(nextpos->time - prevpos->time);
+ pos[0] = prevpos->param[0] + (nextpos->param[0] - prevpos->param[0])*t;
+ pos[1] = prevpos->param[1] + (nextpos->param[1] - prevpos->param[1])*t;
+ pos[2] = prevpos->param[2] + (nextpos->param[2] - prevpos->param[2])*t;
+ }
+ else
+ memcpy (pos, prevpos->param, sizeof(float[3]));
+
+ if ((nextrot != NULL) && (prevrot->time != nTime))
+ {
+ // TODO: USE KEY IN/OUT WEIGHTS
+ float t = (float)(nTime - prevrot->time)/(nextrot->time - prevrot->time);
+ rot[0] = prevrot->param[0] + (nextrot->param[0] - prevrot->param[0])*t;
+ rot[1] = prevrot->param[1] + (nextrot->param[1] - prevrot->param[1])*t;
+ rot[2] = prevrot->param[2] + (nextrot->param[2] - prevrot->param[2])*t;
+ rot[3] = prevrot->param[3] + (nextrot->param[3] - prevrot->param[3])*t;
+ }
+ else
+ memcpy (rot, prevrot->param, sizeof(float[4]));
+ }
+ else
+ {
+ if (memcmp(pos, prevpos->param, sizeof(float[3])) ||
+ memcmp(rot, prevrot->param, sizeof(float[4])))
+ {
+ memcpy (pos, prevpos->param, sizeof(float[3]));
+ memcpy (rot, prevrot->param, sizeof(float[4]));
+ }
+ else
+ return false;
+ }
+
+ return true;
+}
+
+Group* Piece::GetTopGroup()
+{
+ return m_pGroup ? m_pGroup->GetTopGroup() : NULL;
+}
+
+void Piece::DoGroup(Group* pGroup)
+{
+ if (m_pGroup != NULL && m_pGroup != (Group*)-1 && m_pGroup > (Group*)0xFFFF)
+ m_pGroup->SetGroup(pGroup);
+ else
+ m_pGroup = pGroup;
+}
+
+void Piece::UnGroup(Group* pGroup)
+{
+ if ((m_pGroup == pGroup) || (pGroup == NULL))
+ m_pGroup = NULL;
+ else
+ if (m_pGroup != NULL)
+ m_pGroup->UnGroup(pGroup);
+}
+
+// Recalculates current position and connections
+void Piece::UpdatePosition(unsigned short nTime, bool bAnimation)
+{
+ if (!IsVisible(nTime, bAnimation))
+ m_nState &= ~(LC_PIECE_SELECTED|LC_PIECE_FOCUSED);
+
+ CalculatePositionRotation(nTime, bAnimation, m_fPosition, m_fRotation);
+// if (CalculatePositionRotation(nTime, bAnimation, m_fPosition, m_fRotation))
+ {
+ Matrix mat(m_fRotation, m_fPosition);
+ m_BoundingBox.CalculateBoundingBox(&mat, m_pPieceInfo->m_fDimensions);
+ for (int i = 0; i < m_pPieceInfo->m_nConnectionCount; i++)
+ {
+ mat.TransformPoint(m_pConnections[i].center, m_pPieceInfo->m_pConnections[i].center);
+
+ // TODO: rotate normal
+ }
+ }
+}
+
+void Piece::BuildDrawInfo()
+{
+ if (m_pDrawInfo != NULL)
+ {
+ free(m_pDrawInfo);
+ m_pDrawInfo = NULL;
+ }
+
+ DRAWGROUP* dg;
+ bool add;
+ unsigned short group, colcount, i, j;
+ unsigned long count[LC_COL_DEFAULT+1][3], vert;
+ memset (count, 0, sizeof(count));
+
+ // Get the vertex count
+ for (group = m_pPieceInfo->m_nGroupCount, dg = m_pPieceInfo->m_pGroups; group--; dg++)
+ {
+ unsigned short* sh = dg->connections;
+ add = IsTransparent() || *sh == 0xFFFF;
+
+ if (!add)
+ for (; *sh != 0xFFFF; sh++)
+ if ((m_pConnections[*sh].link == NULL) ||
+ (m_pConnections[*sh].link->owner->IsTransparent()))
+ {
+ add = true;
+ break;
+ }
+
+ if (add)
+ {
+ if (m_pPieceInfo->m_nFlags & LC_PIECE_LONGDATA)
+ {
+ unsigned long* p, curcol, colors;
+ p = (unsigned long*)dg->drawinfo;
+ colors = *p;
+ p++;
+
+ while (colors--)
+ {
+ curcol = *p;
+ p++;
+ count[curcol][0] += *p;
+ p += *p + 1;
+ count[curcol][1] += *p;
+ p += *p + 1;
+ count[curcol][2] += *p;
+ p += *p + 1;
+ }
+ }
+ else
+ {
+ unsigned short* p, curcol, colors;
+ p = (unsigned short*)dg->drawinfo;
+ colors = *p;
+ p++;
+
+ while (colors--)
+ {
+ curcol = *p;
+ p++;
+ count[curcol][0] += *p;
+ p += *p + 1;
+ count[curcol][1] += *p;
+ p += *p + 1;
+ count[curcol][2] += *p;
+ p += *p + 1;
+ }
+ }
+ }
+ }
+
+ colcount = 0;
+ vert = 0;
+ for (i = 0; i < LC_COL_DEFAULT+1; i++)
+ if (count[i][0] || count[i][1] || count[i][2])
+ {
+ colcount++;
+ vert += count[i][0] + count[i][1] + count[i][2];
+ }
+ vert += (colcount*4)+1;
+
+ // Build the info
+ if (m_pPieceInfo->m_nFlags & LC_PIECE_LONGDATA)
+ {
+ m_pDrawInfo = malloc(vert*sizeof(unsigned long));
+ unsigned long* drawinfo = (unsigned long*)m_pDrawInfo;
+ *drawinfo = colcount;
+ drawinfo++;
+ i = LC_COL_DEFAULT;
+
+ for (i = LC_COL_DEFAULT; i != LC_COL_EDGES+1;)
+ {
+ if (count[i][0] || count[i][1] || count[i][2])
+ {
+ *drawinfo = i;
+ drawinfo++;
+
+ for (j = 0; j < 3; j++)
+ {
+ *drawinfo = count[i][j];
+ drawinfo++;
+
+ if (count[i][j] == 0)
+ continue;
+
+ for (group = m_pPieceInfo->m_nGroupCount, dg = m_pPieceInfo->m_pGroups; group--; dg++)
+ {
+ unsigned short* sh = dg->connections;
+ add = IsTransparent() || *sh == 0xFFFF;
+
+ if (!add)
+ for (; *sh != 0xFFFF; sh++)
+ if ((m_pConnections[*sh].link == NULL) ||
+ (m_pConnections[*sh].link->owner->IsTransparent()))
+ {
+ add = true;
+ break;
+ }
+
+ if (!add)
+ continue;
+
+ unsigned long* p, colors;
+ p = (unsigned long*)dg->drawinfo;
+ colors = *p;
+ p++;
+
+ while (colors--)
+ {
+ if (*p == i)
+ {
+ p++;
+
+ if (j == 0)
+ {
+ memcpy(drawinfo, p+1, (*p)*sizeof(unsigned long));
+ drawinfo += *p;
+ }
+ p += *p + 1;
+
+ if (j == 1)
+ {
+ memcpy(drawinfo, p+1, (*p)*sizeof(unsigned long));
+ drawinfo += *p;
+ }
+ p += *p + 1;
+
+ if (j == 2)
+ {
+ memcpy(drawinfo, p+1, (*p)*sizeof(unsigned long));
+ drawinfo += *p;
+ }
+ p += *p + 1;
+ }
+ else
+ {
+ p++;
+ p += *p + 1;
+ p += *p + 1;
+ p += *p + 1;
+ }
+ }
+ }
+ }
+ }
+
+ if (i == LC_COL_DEFAULT)
+ i = 0;
+ else
+ i++;
+ }
+ }
+ else
+ {
+ m_pDrawInfo = malloc(vert*sizeof(unsigned short));
+ unsigned short* drawinfo = (unsigned short*)m_pDrawInfo;
+ *drawinfo = colcount;
+ drawinfo++;
+
+ for (i = LC_COL_DEFAULT; i != LC_COL_EDGES+1;)
+ {
+ if (count[i][0] || count[i][1] || count[i][2])
+ {
+ *drawinfo = i;
+ drawinfo++;
+
+ for (j = 0; j < 3; j++)
+ {
+ *drawinfo = (unsigned short)count[i][j];
+ drawinfo++;
+
+ if (count[i][j] == 0)
+ continue;
+
+ for (group = m_pPieceInfo->m_nGroupCount, dg = m_pPieceInfo->m_pGroups; group--; dg++)
+ {
+ unsigned short* sh = dg->connections;
+ add = IsTransparent() || *sh == 0xFFFF;
+
+ if (!add)
+ for (; *sh != 0xFFFF; sh++)
+ if ((m_pConnections[*sh].link == NULL) ||
+ (m_pConnections[*sh].link->owner->IsTransparent()))
+ {
+ add = true;
+ break;
+ }
+
+ if (!add)
+ continue;
+
+ unsigned short* p, colors;
+ p = (unsigned short*)dg->drawinfo;
+ colors = *p;
+ p++;
+
+ while (colors--)
+ {
+ if (*p == i)
+ {
+ p++;
+
+ if (j == 0)
+ {
+ memcpy(drawinfo, p+1, (*p)*sizeof(unsigned short));
+ drawinfo += *p;
+ }
+ p += *p + 1;
+
+ if (j == 1)
+ {
+ memcpy(drawinfo, p+1, (*p)*sizeof(unsigned short));
+ drawinfo += *p;
+ }
+ p += *p + 1;
+
+ if (j == 2)
+ {
+ memcpy(drawinfo, p+1, (*p)*sizeof(unsigned short));
+ drawinfo += *p;
+ }
+ p += *p + 1;
+ }
+ else
+ {
+ p++;
+ p += *p + 1;
+ p += *p + 1;
+ p += *p + 1;
+ }
+ }
+ }
+ }
+ }
+
+ if (i == LC_COL_DEFAULT)
+ i = 0;
+ else
+ i++;
+ }
+ }
+}
+
+void Piece::Render(bool bLighting, bool bNoAlpha, bool bEdges, unsigned char* nLastColor, bool* bTrans)
+{
+ glPushMatrix();
+ glTranslatef(m_fPosition[0], m_fPosition[1], m_fPosition[2]);
+ glRotatef(m_fRotation[3], m_fRotation[0], m_fRotation[1], m_fRotation[2]);
+ glVertexPointer (3, GL_FLOAT, 0, m_pPieceInfo->m_fVertexArray);
+
+// glEnable(GL_POLYGON_STIPPLE);
+
+ for (int sh = 0; sh < m_pPieceInfo->m_nTextureCount; sh++)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+ m_pPieceInfo->m_pTextures[sh].texture->MakeCurrent();
+
+ if (m_pPieceInfo->m_pTextures[sh].color == LC_COL_DEFAULT)
+ {
+ SetCurrentColor(m_nColor, bTrans, bLighting, bNoAlpha);
+ *nLastColor = m_nColor;
+ }
+
+ glEnable(GL_TEXTURE_2D);
+ glBegin(GL_QUADS);
+ glTexCoord2fv(m_pPieceInfo->m_pTextures[sh].coords[0]);
+ glVertex3fv(m_pPieceInfo->m_pTextures[sh].vertex[0]);
+ glTexCoord2fv(m_pPieceInfo->m_pTextures[sh].coords[1]);
+ glVertex3fv(m_pPieceInfo->m_pTextures[sh].vertex[1]);
+ glTexCoord2fv(m_pPieceInfo->m_pTextures[sh].coords[2]);
+ glVertex3fv(m_pPieceInfo->m_pTextures[sh].vertex[2]);
+ glTexCoord2fv(m_pPieceInfo->m_pTextures[sh].coords[3]);
+ glVertex3fv(m_pPieceInfo->m_pTextures[sh].vertex[3]);
+ glEnd();
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ if (m_pPieceInfo->m_nFlags & LC_PIECE_LONGDATA)
+ {
+ unsigned long colors, *info = (unsigned long*)m_pDrawInfo;
+ colors = *info;
+ info++;
+
+ while (colors--)
+ {
+ bool lock = lockarrays && (*info == LC_COL_DEFAULT || *info == LC_COL_EDGES);
+
+ if (*info != *nLastColor)
+ {
+ if (*info == LC_COL_DEFAULT)
+ {
+ SetCurrentColor(m_nColor, bTrans, bLighting, bNoAlpha);
+ *nLastColor = m_nColor;
+ }
+ else
+ {
+ SetCurrentColor((unsigned char)*info, bTrans, bLighting, bNoAlpha);
+ *nLastColor = (unsigned char)*info;
+ }
+ }
+ info++;
+
+ if (lock)
+ glLockArraysEXT(0, m_pPieceInfo->m_nVertexCount);
+
+ if (*info)
+ {
+ glDrawElements(GL_QUADS, *info, GL_UNSIGNED_INT, info+1);
+ info += *info + 1;
+ }
+ else
+ info++;
+
+ if (*info)
+ {
+ glDrawElements(GL_TRIANGLES, *info, GL_UNSIGNED_INT, info+1);
+ info += *info + 1;
+ }
+ else
+ info++;
+
+ if (*info)
+ {
+ if (m_nState & LC_PIECE_SELECTED)
+ {
+ SetCurrentColor(m_nState & LC_PIECE_FOCUSED ? LC_COL_FOCUSED : LC_COL_SELECTED, bTrans, bLighting, bNoAlpha);
+ *nLastColor = m_nState & LC_PIECE_FOCUSED ? LC_COL_FOCUSED : LC_COL_SELECTED;
+ glDrawElements(GL_LINES, *info, GL_UNSIGNED_INT, info+1);
+ }
+ else
+ if (bEdges)
+ glDrawElements(GL_LINES, *info, GL_UNSIGNED_INT, info+1);
+
+ info += *info + 1;
+ }
+ else
+ info++;
+
+ if (lock)
+ glUnlockArraysEXT();
+ }
+ }
+ else
+ {
+ unsigned short colors, *info = (unsigned short*)m_pDrawInfo;
+ colors = *info;
+ info++;
+
+ while (colors--)
+ {
+ bool lock = lockarrays && (*info == LC_COL_DEFAULT || *info == LC_COL_EDGES);
+
+ if (*info != *nLastColor)
+ {
+ if (*info == LC_COL_DEFAULT)
+ {
+ SetCurrentColor(m_nColor, bTrans, bLighting, bNoAlpha);
+ *nLastColor = m_nColor;
+ }
+ else
+ {
+ SetCurrentColor((unsigned char)*info, bTrans, bLighting, bNoAlpha);
+ *nLastColor = (unsigned char)*info;
+ }
+ }
+ info++;
+
+ if (lock)
+ glLockArraysEXT(0, m_pPieceInfo->m_nVertexCount);
+
+ if (*info)
+ {
+ glDrawElements(GL_QUADS, *info, GL_UNSIGNED_SHORT, info+1);
+ info += *info + 1;
+ }
+ else
+ info++;
+
+ if (*info)
+ {
+ glDrawElements(GL_TRIANGLES, *info, GL_UNSIGNED_SHORT, info+1);
+ info += *info + 1;
+ }
+ else
+ info++;
+
+ if (*info)
+ {
+ if (m_nState & LC_PIECE_SELECTED)
+ {
+ SetCurrentColor(m_nState & LC_PIECE_FOCUSED ? LC_COL_FOCUSED : LC_COL_SELECTED, bTrans, bLighting, bNoAlpha);
+ *nLastColor = m_nState & LC_PIECE_FOCUSED ? LC_COL_FOCUSED : LC_COL_SELECTED;
+ glDrawElements(GL_LINES, *info, GL_UNSIGNED_SHORT, info+1);
+ }
+ else
+ if (bEdges)
+ glDrawElements(GL_LINES, *info, GL_UNSIGNED_SHORT, info+1);
+
+ info += *info + 1;
+ }
+ else
+ info++;
+
+ if (lock)
+ glUnlockArraysEXT();
+ }
+ }
+
+ glPopMatrix();
+}
+
+void Piece::CalculateConnections(CONNECTION_TYPE* pConnections, unsigned short nTime, bool bAnimation, bool bForceRebuild, bool bFixOthers)
+{
+ if (m_pConnections == NULL)
+ {
+ if (m_pDrawInfo == NULL)
+ BuildDrawInfo();
+ return;
+ }
+
+ bool rebuild = bForceRebuild || (m_pDrawInfo == NULL);
+ Piece* pPiece;
+ CONNECTION_ENTRY* entry;
+ int i, j, c;
+
+ if (bFixOthers)
+ m_pLink = NULL;
+
+ for (j = 0; j < m_pPieceInfo->m_nConnectionCount; j++)
+ {
+ CONNECTION* new_link = NULL;
+
+ // studs
+ if (m_pConnections[j].type == 0)
+ {
+ i = pConnections[1].numentries;
+ entry = pConnections[1].entries;
+
+ for (; i--; entry++)
+ {
+ if ((entry->owner == this) ||
+ (!entry->owner->IsVisible(nTime, bAnimation)))
+ continue;
+
+ for (c = 0; c < entry->numcons; c++)
+ {
+ CONNECTION* con = entry->cons[c];
+
+ if (((m_pConnections[j].center[0] - con->center[0]) < 0.1f) &&
+ ((m_pConnections[j].center[1] - con->center[1]) < 0.1f) &&
+ ((m_pConnections[j].center[2] - con->center[2]) < 0.1f) &&
+ ((m_pConnections[j].center[0] - con->center[0]) > -0.1f) &&
+ ((m_pConnections[j].center[1] - con->center[1]) > -0.1f) &&
+ ((m_pConnections[j].center[2] - con->center[2]) > -0.1f))
+ {
+ new_link = con;
+ i = 0;
+ break;
+ }
+ }
+ }
+
+ if (new_link != m_pConnections[j].link)
+ {
+ if ((m_pConnections[j].link != NULL) != (new_link != NULL))
+ rebuild = true;
+
+ if (bFixOthers)
+ {
+ // Update old connection
+ if (m_pConnections[j].link != NULL)
+ {
+ Piece* pOwner = m_pConnections[j].link->owner;
+
+ if (pOwner != this)
+ {
+ if (m_pLink == NULL)
+ {
+ m_pLink = pOwner;
+ pOwner->m_pLink = NULL;
+ }
+ else
+ for (pPiece = m_pLink; pPiece; pPiece = pPiece->m_pLink)
+ {
+ if (pPiece == pOwner)
+ break;
+
+ if (pPiece->m_pLink == NULL)
+ {
+ pPiece->m_pLink = pOwner;
+ pOwner->m_pLink = NULL;
+ }
+ }
+ }
+
+ if (new_link)
+ {
+ pOwner = new_link->owner;
+
+ if (m_pLink == NULL)
+ {
+ m_pLink = pOwner;
+ pOwner->m_pLink = NULL;
+ }
+ else
+ for (pPiece = m_pLink; pPiece; pPiece = pPiece->m_pLink)
+ {
+ if (pPiece == pOwner)
+ break;
+
+ if (pPiece->m_pLink == NULL)
+ {
+ pPiece->m_pLink = pOwner;
+ pOwner->m_pLink = NULL;
+ }
+ }
+ }
+ }
+ }
+
+ m_pConnections[j].link = new_link;
+ }
+
+ continue;
+ }
+
+ // invert studs
+ if (m_pConnections[j].type == 1)
+ {
+ i = pConnections[0].numentries;
+ entry = pConnections[0].entries;
+
+ for (; i--; entry++)
+ {
+ if ((entry->owner == this) ||
+ (!entry->owner->IsVisible(nTime, bAnimation)))
+ continue;
+
+ for (c = 0; c < entry->numcons; c++)
+ {
+ CONNECTION* con = entry->cons[c];
+
+ if (((m_pConnections[j].center[0] - con->center[0]) < 0.1f) &&
+ ((m_pConnections[j].center[1] - con->center[1]) < 0.1f) &&
+ ((m_pConnections[j].center[2] - con->center[2]) < 0.1f) &&
+ ((m_pConnections[j].center[0] - con->center[0]) > -0.1f) &&
+ ((m_pConnections[j].center[1] - con->center[1]) > -0.1f) &&
+ ((m_pConnections[j].center[2] - con->center[2]) > -0.1f))
+ {
+ new_link = con;
+ i = 0;
+ break;
+ }
+ }
+ }
+
+ if (new_link != m_pConnections[j].link)
+ {
+ if ((m_pConnections[j].link != NULL) != (new_link != NULL))
+ rebuild = true;
+
+ if (bFixOthers)
+ {
+ Piece* pOwner;
+
+ // Update old connection
+ if (m_pConnections[j].link != NULL)
+ {
+ pOwner = m_pConnections[j].link->owner;
+
+ if (pOwner != this)
+ {
+ if (m_pLink == NULL)
+ {
+ m_pLink = pOwner;
+ pOwner->m_pLink = NULL;
+ }
+ else
+ for (pPiece = m_pLink; pPiece; pPiece = pPiece->m_pLink)
+ {
+ if (pPiece == pOwner)
+ break;
+
+ if (pPiece->m_pLink == NULL)
+ {
+ pPiece->m_pLink = pOwner;
+ pOwner->m_pLink = NULL;
+ }
+ }
+ }
+ }
+
+ if (new_link)
+ {
+ pOwner = new_link->owner;
+
+ if (m_pLink == NULL)
+ {
+ m_pLink = pOwner;
+ pOwner->m_pLink = NULL;
+ }
+ else
+ for (pPiece = m_pLink; pPiece; pPiece = pPiece->m_pLink)
+ {
+ if (pPiece == pOwner)
+ break;
+
+ if (pPiece->m_pLink == NULL)
+ {
+ pPiece->m_pLink = pOwner;
+ pOwner->m_pLink = NULL;
+ }
+ }
+ }
+ }
+
+ m_pConnections[j].link = new_link;
+ }
+ else
+ {
+ if (bFixOthers && bForceRebuild)
+ {
+ if (!m_pConnections[j].link)
+ continue;
+
+ Piece* pOwner = m_pConnections[j].link->owner;
+
+ if (m_pLink == NULL)
+ {
+ m_pLink = pOwner;
+ pOwner->m_pLink = NULL;
+ }
+ else
+ for (pPiece = m_pLink; pPiece; pPiece = pPiece->m_pLink)
+ {
+ if (pPiece == pOwner)
+ break;
+
+ if (pPiece->m_pLink == NULL)
+ {
+ pPiece->m_pLink = pOwner;
+ pOwner->m_pLink = NULL;
+ }
+ }
+ }
+ }
+
+ continue;
+ }
+ }
+
+ if (bFixOthers)
+ for (pPiece = m_pLink; pPiece; pPiece = pPiece->m_pLink)
+ pPiece->CalculateConnections(pConnections, nTime, bAnimation, true, false);
+
+/*
+ BOOL bRebuild = FALSE;
+ CONNECTION *c1, *c2;
+ int sz = sizeof(CPiece*)*(m_pInfo->m_nConnectionCount-1);
+ CPiece** pConnections = (CPiece**)malloc(sz);
+ memset(pConnections, 0, sz);
+
+ for (POSITION pos = pDoc->m_Pieces.GetHeadPosition(); pos != NULL;)
+ {
+ CPiece* pPiece = pDoc->m_Pieces.GetNext(pos);
+ if ((pPiece == this) || (pPiece->m_pInfo->m_nConnectionCount == 1) ||
+ (!pPiece->IsVisible(nTime, bAnimator)))
+ continue;
+ pPiece->m_bUpdated = FALSE;
+
+ for (i = 0; i < m_pInfo->m_nConnectionCount-1; i++)
+ {
+ c1 = &m_pInfo->m_pConnections[i+1];
+
+ for (j = 0; j < pPiece->m_pInfo->m_nConnectionCount-1; j++)
+ {
+ c2 = &pPiece->m_pInfo->m_pConnections[j+1];
+
+ if (ConnectionsMatch(c1->type, c2->type))
+ {
+// normal
+ if ((fabs(m_pConnections[i].pos[0]-pPiece->m_pConnections[j].pos[0]) < 0.1) &&
+ (fabs(m_pConnections[i].pos[1]-pPiece->m_pConnections[j].pos[1]) < 0.1) &&
+ (fabs(m_pConnections[i].pos[2]-pPiece->m_pConnections[j].pos[2]) < 0.1))
+ {
+ pConnections[i] = pPiece;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < m_pInfo->m_nConnectionCount-1; i++)
+ if (m_pConnections[i].pPiece != pConnections[i])
+ {
+ if (bOthers)
+ {
+ if ((m_pConnections[i].pPiece != NULL) &&
+ (m_pConnections[i].pPiece->IsVisible(nTime, bAnimator)))
+ m_pConnections[i].pPiece->UpdateConnections(this);
+ if ((pConnections[i] != NULL) &&
+ (pConnections[i]->IsVisible(nTime, bAnimator)))
+ pConnections[i]->UpdateConnections(this);
+ }
+
+ if (m_pConnections[i].pPiece == NULL)
+ {
+ if (m_pInfo->m_pConnections[i].info != NULL)
+ bRebuild = TRUE;
+ }
+ else
+ {
+ if (pConnections[i] == NULL)
+ if (m_pInfo->m_pConnections[i].info != NULL)
+ bRebuild = TRUE;
+ }
+
+ m_pConnections[i].pPiece = pConnections[i];
+ }
+
+ free(pConnections);
+*/
+ if (rebuild)
+ BuildDrawInfo();
+}
+
+/*
+inline static BOOL ConnectionsMatch(BYTE c1, BYTE c2)
+{
+ if (c1 == 1)
+ {
+ if (c2 == 2)
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ if (c2 == 1)
+ {
+ if (c1 == 2)
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ // 1: STUD
+ // 2: STUD CONNECTION
+// int i = __min (c1, c2);
+// int j = __max (c1, c2);
+// switch (i)
+// {
+// case 1: if (j == 2) return TRUE;
+// }
+
+ return FALSE;
+}
+
+void CPiece::UpdateConnections(CPiece* pPiece)
+{
+ if (m_bUpdated || m_pInfo->m_nConnectionCount == 1)
+ return;
+ BOOL bRebuild = FALSE;
+ int sz = sizeof(CPiece*)*(m_pInfo->m_nConnectionCount-1), i, j;
+ CONNECTION *c1, *c2;
+ CPiece** pConnections = (CPiece**)malloc(sz);
+ memset(pConnections, 0, sz);
+
+ for (i = 0; i < m_pInfo->m_nConnectionCount-1; i++)
+ {
+ c1 = &m_pInfo->m_pConnections[i+1];
+
+ for (j = 0; j < pPiece->m_pInfo->m_nConnectionCount-1; j++)
+ {
+ c2 = &pPiece->m_pInfo->m_pConnections[j+1];
+
+ if (ConnectionsMatch(c1->type, c2->type))
+ {
+// normal
+ if ((fabs(m_pConnections[i].pos[0]-pPiece->m_pConnections[j].pos[0]) < 0.1) &&
+ (fabs(m_pConnections[i].pos[1]-pPiece->m_pConnections[j].pos[1]) < 0.1) &&
+ (fabs(m_pConnections[i].pos[2]-pPiece->m_pConnections[j].pos[2]) < 0.1))
+ {
+ pConnections[i] = pPiece;
+ break;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < m_pInfo->m_nConnectionCount-1; i++)
+ {
+ if (m_pConnections[i].pPiece == pPiece && pConnections[i] == NULL)
+ {
+ m_pConnections[i].pPiece = NULL;
+ bRebuild = TRUE;
+ }
+
+ if (pConnections[i] == pPiece && m_pConnections[i].pPiece == NULL)
+ {
+ m_pConnections[i].pPiece = pPiece;
+ bRebuild = TRUE;
+ }
+ }
+
+ if (bRebuild)
+ BuildDrawInfo();
+ free(pConnections);
+ m_bUpdated = TRUE;
+}
+*/
+
+void Piece::AddConnections(CONNECTION_TYPE* pConnections)
+{
+ int i, j, c;
+
+ for (i = 0; i < LC_CONNECTIONS; i++)
+ {
+ c = 0;
+
+ for (j = 0; j < m_pPieceInfo->m_nConnectionCount; j++)
+ if (m_pConnections[j].type == i)
+ c++;
+
+ if (c > 0)
+ {
+ // check if we need to realloc
+ if (pConnections[i].numentries % 5 == 0)
+ {
+ if (pConnections[i].numentries > 0)
+ pConnections[i].entries = (CONNECTION_ENTRY*)realloc(pConnections[i].entries, sizeof(CONNECTION_ENTRY)*(pConnections[i].numentries+5));
+ else
+ pConnections[i].entries = (CONNECTION_ENTRY*)realloc(pConnections[i].entries, sizeof(CONNECTION_ENTRY)*5);
+ }
+
+ CONNECTION_ENTRY* entry = &pConnections[i].entries[pConnections[i].numentries];
+ pConnections[i].numentries++;
+
+ entry->owner = this;
+ entry->numcons = c;
+ entry->cons = (CONNECTION**)malloc(c*sizeof(CONNECTION*));
+
+ c = 0;
+ for (j = 0; j < m_pPieceInfo->m_nConnectionCount; j++)
+ if (m_pConnections[j].type == i)
+ {
+ entry->cons[c] = &m_pConnections[j];
+ c++;
+ }
+ }
+ }
+}
+
+void Piece::RemoveConnections(CONNECTION_TYPE* pConnections)
+{
+ int i, j;
+
+ for (i = 0; i < LC_CONNECTIONS; i++)
+ for (j = 0; j < pConnections[i].numentries; j++)
+ if (pConnections[i].entries[j].owner == this)
+ {
+ free(pConnections[i].entries[j].cons);
+ pConnections[i].numentries--;
+
+ for (; j < pConnections[i].numentries; j++)
+ pConnections[i].entries[j] = pConnections[i].entries[j+1];
+
+ if (pConnections[i].numentries % 5 == 0)
+ {
+ if (pConnections[i].numentries > 0)
+ pConnections[i].entries = (CONNECTION_ENTRY*)realloc(pConnections[i].entries, sizeof(CONNECTION_ENTRY)*pConnections[i].numentries);
+ else
+ {
+ free (pConnections[i].entries);
+ pConnections[i].entries = NULL;
+ }
+ }
+ }
+}
+
+/*
+// Performs exact collision detection using the RAPID library.
+// Check if there are 2 objects at the same place at the same time
+BOOL CPiece::Collide(CPiece* pPiece)
+{
+ return CollideAt(pPiece, m_fRotation, m_fPosition);
+}
+
+// Use this to check if the piece can be modified
+BOOL CPiece::CollideAt(CPiece* pPiece, float rot[4], float pos[3])
+{
+ double rotation1[3][3], rotation2[3][3];
+ double translation1[3], translation2[3];
+
+ CMatrix m1(rot, pos);
+ rotation1[0][0] = m1.m[0];
+ rotation1[0][1] = m1.m[4];
+ rotation1[0][2] = m1.m[8];
+ rotation1[1][0] = m1.m[1];
+ rotation1[1][1] = m1.m[5];
+ rotation1[1][2] = m1.m[9];
+ rotation1[2][0] = m1.m[2];
+ rotation1[2][1] = m1.m[6];
+ rotation1[2][2] = m1.m[10];
+ translation1[0] = m1.m[12];
+ translation1[1] = m1.m[13];
+ translation1[2] = m1.m[14];
+
+ CMatrix m2(pPiece->m_fRotation, pPiece->m_fPosition);
+ rotation2[0][0] = m2.m[0];
+ rotation2[0][1] = m2.m[4];
+ rotation2[0][2] = m2.m[8];
+ rotation2[1][0] = m2.m[1];
+ rotation2[1][1] = m2.m[5];
+ rotation2[1][2] = m2.m[9];
+ rotation2[2][0] = m2.m[2];
+ rotation2[2][1] = m2.m[6];
+ rotation2[2][2] = m2.m[10];
+ translation2[0] = m2.m[12];
+ translation2[1] = m2.m[13];
+ translation2[2] = m2.m[14];
+
+ return CollisionCheck(rotation1, translation1, m_pInfo->m_pRModel,
+ rotation2, translation2, pPiece->m_pInfo->m_pRModel);
+}
+*/
diff --git a/common/piece.h b/common/piece.h
new file mode 100644
index 0000000..bf6089d
--- /dev/null
+++ b/common/piece.h
@@ -0,0 +1,186 @@
+//
+// piece.h
+////////////////////////////////////////////////////
+
+#ifndef _PIECE_H_
+#define _PIECE_H_
+
+class File;
+class Piece;
+class Group;
+class PieceInfo;
+
+#include "boundbox.h"
+#include "globals.h"
+#include "typedefs.h"
+#include "defines.h"
+
+#define LC_PIECE_HIDDEN 0x01
+#define LC_PIECE_SELECTED 0x02
+#define LC_PIECE_FOCUSED 0x04
+
+typedef enum { PK_POSITION, PK_ROTATION } PK_TYPES;
+
+typedef struct PIECE_KEY {
+ unsigned short time;
+ float param[4];
+ unsigned char type;
+ PIECE_KEY* next;
+} PIECE_KEY;
+
+class Piece
+{
+public:
+ Piece(PieceInfo* pPieceInfo);
+ ~Piece();
+
+ Piece* m_pNext;
+ Piece* m_pLink;
+
+ void Hide()
+ { m_nState = LC_PIECE_HIDDEN; }
+ void UnHide()
+ { m_nState &= ~LC_PIECE_HIDDEN; }
+ bool IsHidden()
+ { return (m_nState & LC_PIECE_HIDDEN) != 0; }
+ void Select()
+ { m_nState |= LC_PIECE_SELECTED; }
+ void UnSelect()
+ { m_nState &= ~(LC_PIECE_SELECTED|LC_PIECE_FOCUSED); }
+ bool IsSelected()
+ { return (m_nState & LC_PIECE_SELECTED) != 0; }
+ void Focus()
+ { m_nState |= LC_PIECE_FOCUSED|LC_PIECE_SELECTED; }
+ void UnFocus()
+ { m_nState &= ~LC_PIECE_FOCUSED; }
+ bool IsFocused()
+ { return (m_nState & LC_PIECE_FOCUSED) != 0; }
+
+ void MinIntersectDist(CLICKLINE* pLine);
+ bool IsVisible(unsigned short nTime, bool bAnimation);
+ void Initialize(float x, float y, float z, unsigned char nStep, unsigned short nFrame, unsigned char nColor);
+ void CreateName(Piece* pPiece);
+ void AddConnections(CONNECTION_TYPE* pConnections);
+ void RemoveConnections(CONNECTION_TYPE* pConnections);
+ void CompareBoundingBox(float box[6]);
+ void SetPieceInfo(PieceInfo* pPieceInfo);
+ void FileLoad(File* file, char* name);
+ void FileSave(File* file, Group* pGroups);
+
+ void CalculateConnections(CONNECTION_TYPE* pConnections, unsigned short nTime, bool bAnimation, bool bForceRebuild, bool bFixOthers);
+ void UpdatePosition(unsigned short nTime, bool bAnimation);
+ bool CalculatePositionRotation(unsigned short nTime, bool bAnimation, float pos[3], float rot[4]);
+ void Move(unsigned short nTime, bool bAnimation, bool bAddKey, float x, float y, float z);
+ void ChangeKey(unsigned short nTime, bool bAnimation, bool bAddKey, float* param, unsigned char nKeyType);
+ void DoGroup(Group* pGroup);
+ void UnGroup(Group* pGroup);
+ Group* GetTopGroup();
+ void SetGroup(Group* pGroup)
+ { m_pGroup = pGroup; }
+ Group* GetGroup()
+ { return m_pGroup; }
+ void SetName(char* name)
+ { strcpy(m_strName, name); }
+ const char* GetName()
+ { return m_strName; }
+ const unsigned char GetColor()
+ { return m_nColor; }
+ void SetColor(unsigned char color)
+ { m_nColor = color; }
+ PieceInfo* GetPieceInfo()
+ { return m_pPieceInfo; }
+ void SetStepShow(unsigned char step)
+ { m_nStepShow = step; }
+ const unsigned char GetStepShow()
+ { return m_nStepShow; }
+ void SetStepHide(unsigned char step)
+ { m_nStepHide = step; }
+ const unsigned char GetStepHide()
+ { return m_nStepHide; }
+ void SetFrameShow(unsigned short frame)
+ { m_nFrameShow = frame; }
+ const unsigned short GetFrameShow()
+ { return m_nFrameShow; }
+ void SetFrameHide(unsigned short frame)
+ { m_nFrameHide = frame; }
+ const unsigned short GetFrameHide()
+ { return m_nFrameHide; }
+ const float* GetConstPosition()
+ { return m_fPosition; }
+ void GetPosition (float* position)
+ { memcpy(position, m_fPosition, sizeof(m_fPosition)); }
+ void GetRotation (float* rotation)
+ { memcpy(rotation, m_fRotation, sizeof(m_fRotation)); }
+
+ void Render(bool bLighting, bool bNoAlpha, bool bEdges, unsigned char* nLastColor, bool* bTrans);
+ inline void RenderBox(bool bHilite, float fLineWidth)
+ {
+ glPushMatrix();
+ glTranslatef(m_fPosition[0], m_fPosition[1], m_fPosition[2]);
+ glRotatef(m_fRotation[3], m_fRotation[0], m_fRotation[1], m_fRotation[2]);
+
+ if (bHilite && ((m_nState & LC_PIECE_SELECTED) != 0))
+ {
+ glColor3fv(FlatColorArray[m_nState & LC_PIECE_FOCUSED ? LC_COL_FOCUSED : LC_COL_SELECTED]);
+ glLineWidth(2*fLineWidth);
+ glPushAttrib(GL_POLYGON_BIT);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ glCallList(m_nBoxList);
+ glPopAttrib();
+ glLineWidth(fLineWidth);
+ }
+ else
+ {
+ glColor3fv (FlatColorArray[m_nColor]);
+ glCallList(m_nBoxList);
+ }
+ glPopMatrix();
+ }
+
+ inline bool IsTransparent()
+ {
+ if (m_nColor < 14) return false;
+ if (m_nColor > 21) return false;
+ return true;
+ };
+
+/*
+ inline void UseTransform()
+ {
+ glTranslatef(m_fPosition[0], m_fPosition[1], m_fPosition[2]);
+ glRotatef(m_fRotation[3], m_fRotation[0], m_fRotation[1], m_fRotation[2]);
+ }
+*/
+protected:
+ void LineFacet(float* p1, float* p2, float* p3, float* p4, CLICKLINE* pLine);
+ void RemoveKeys();
+ void BuildDrawInfo();
+
+ // Position
+ PIECE_KEY* m_pAnimationKeys;
+ PIECE_KEY* m_pInstructionKeys;
+
+ // Atributes
+ PieceInfo* m_pPieceInfo;
+ BoundingBox m_BoundingBox;
+ Group* m_pGroup;
+
+ unsigned short m_nFrameShow;
+ unsigned short m_nFrameHide;
+ unsigned char m_nStepShow;
+ unsigned char m_nStepHide;
+
+ unsigned char m_nColor;
+ unsigned char m_nState;
+ char m_strName[81];
+
+ // Temporary variables
+ float m_fPosition[3];
+ float m_fRotation[4];
+ GLuint m_nBoxList;
+ CONNECTION* m_pConnections;
+ void* m_pDrawInfo;
+};
+
+
+#endif // _PIECE_H
diff --git a/common/pieceinf.cpp b/common/pieceinf.cpp
new file mode 100644
index 0000000..5b1e5d0
--- /dev/null
+++ b/common/pieceinf.cpp
@@ -0,0 +1,1818 @@
+// Information about how to draw a piece and some more stuff.
+//
+
+#ifdef _WINDOWS
+#include "stdafx.h"
+#else
+#include <GL/gl.h>
+#include <GL/glu.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h> // TODO: remove, only needed by ZoomExtents()
+#include "texture.h"
+#include "pieceinf.h"
+#include "project.h"
+#include "globals.h"
+#include "matrix.h"
+#include "defines.h"
+
+#define SIDES 16
+static float sintbl[SIDES];
+static float costbl[SIDES];
+
+#define LC_MESH 1
+#define LC_STUD 2
+#define LC_STUD2 3
+#define LC_STUD3 4
+#define LC_STUD4 5
+
+// measurements (in centimeters)
+//#define LC_FLAT_HEIGHT 0.32f
+//#define LC_BRICK_HEIGHT (3*LC_FLAT_HEIGHT)
+//#define LC_BASEPLATE_HEIGHT (LC_FLAT_HEIGHT/2)
+//#define LC_HALF_WIDE 0.4f
+//#define LC_ONE_WIDE 0.8f
+//#define LC_BRICK_WALL 0.125f
+#define LC_STUD_HEIGHT 0.16f
+#define LC_STUD_RADIUS 0.24f
+#define LC_KNOB_RADIUS 0.32f
+//#define LC_STUD_TECH_RADIUS (LC_FLAT_HEIGHT/2)
+
+
+/*
+static unsigned long GetDefaultPieceGroup(char* name)
+{
+ char tmp[9];
+ strncpy (tmp, name, 9);
+// tmp[8] = 0;
+
+ if (strstr(tmp,"Baseplate") || strstr(tmp,"Plate") ||
+ strstr(tmp,"Platform"))
+ return 0x001;
+
+ if (strstr(tmp,"Brick") || strstr(tmp,"Cylin") ||
+ strstr(tmp,"Cone"))
+ return 0x002;
+
+ if (strstr(tmp,"Tile"))
+ return 0x004;
+
+ if (strstr(tmp,"Slope"))
+ return 0x008;
+
+ if (strstr(tmp,"Technic") || strstr(tmp,"Crane") ||
+ strstr(tmp,"Wheel") || strstr(tmp,"Tyre") ||
+ strstr(tmp,"Electric"))
+ return 0x010;
+
+ // space & plane
+ if (strstr(tmp,"Space") || strstr(tmp,"Plane") ||
+ strstr(tmp,"Windscr") || strstr(tmp,"~2421") ||
+ strstr(tmp,"Wing") || strstr(tmp,"Wedge") ||
+ strstr(tmp,"Propellor") || strstr(tmp,"Rotor") ||
+ strstr(tmp,"Rack") || strstr(tmp,"Tail"))
+ return 0x020;
+
+ if (strstr(tmp,"Train"))
+ return 0x040;
+
+ // other parts
+ if (strstr(tmp,"Arch") || strstr(tmp,"Panel") ||
+ strstr(tmp,"Car") || strstr(tmp,"Window") ||
+ strstr(tmp,"Freestyle") || strstr(tmp,"Support")||
+ strstr(tmp,"Fence") || strstr(tmp,"Gate") ||
+ strstr(tmp,"Garage") || strstr(tmp,"Stairs") ||
+ strstr(tmp,"Bracket") || strstr(tmp,"Hinge") ||
+ strstr(tmp,"Homemaker") || strstr(tmp,"Rock") ||
+ strstr(tmp,"Cupboard") || strstr(tmp,"Storage")||
+ strstr(tmp,"Scala") || strstr(tmp,"Boat") ||
+ strstr(tmp,"Trailer") || strstr(tmp,"Box") ||
+ strstr(tmp,"Turntab") || strstr(tmp,"Winch") ||
+ strstr(tmp,"Door") || strstr(tmp,"Magnet"))
+ return 0x080;
+
+ // accessories
+ if (strstr(tmp,"Minifig") || strstr(tmp,"Antenna")||
+ strstr(tmp,"Ladder") || strstr(tmp,"Jack") ||
+ strstr(tmp,"Exhaust") || strstr(tmp,"Lever") ||
+ strstr(tmp,"Roadsign") || strstr(tmp,"Town") ||
+ strstr(tmp,"Leaves") || strstr(tmp,"Horse") ||
+ strstr(tmp,"Tree") || strstr(tmp,"Flower") ||
+ strstr(tmp,"Plant") ||
+ strstr(tmp,"Conveyor") || strstr(tmp,"Tractor")||
+ strstr(tmp,"Grab") || strstr(tmp,"Roller") ||
+ strstr(tmp,"Stretch") || strstr(tmp,"Tap ") ||
+ strstr(tmp,"Forklift") || strstr(tmp,"Flag") ||
+ strstr(tmp,"Belville") || strstr(tmp,"Light &")||
+ strstr(tmp,"Hose") || strstr(tmp,"Arm P") ||
+ strstr(tmp,"Brush") || strstr(tmp,"Castle") ||
+ strstr(tmp,"Tipper") || strstr(tmp,"Bar"))
+ return 0x100;
+
+ return 1;
+}
+*/
+
+// Convert a color from LDraw to LeoCAD
+unsigned char ConvertColor(int c)
+{
+ if (c > 255) c -= 256;
+ switch (c)
+ {
+ case 0: return 9; // black (black)
+ case 1: return 4; // blue (blue)
+ case 2: return 2; // green (green)
+ case 3: return 5; // dark cyan
+ case 4: return 0; // red (red)
+ case 5: return 11; // magenta
+ case 6: return 10; // brown (brown)
+ case 7: return 22; // gray (gray)
+ case 8: return 8; // dark gray (dark gray)
+ case 9: return 5; // light blue ()
+ case 10: return 3; // light green (light green)
+ case 11: return 5; // cyan (light blue)
+ case 12: return 1; // light red
+ case 13: return 11; // pink (pink)
+ case 14: return 6; // yellow (yellow)
+ case 15: return 7; // white (white)
+ case 16: return LC_COL_DEFAULT; // special case
+ case 24: return LC_COL_EDGES; // edge
+ case 32: return 9; // black
+ case 33: return 18; // clear blue
+ case 34: return 16; // clear green
+ case 35: return 5; // dark cyan
+ case 36: return 14; // clear red
+ case 37: return 11; // magenta
+ case 38: return 10; // brown
+ case 39: return 21; // clear white (clear gray)
+ case 40: return 8; // dark gray
+ case 41: return 19; // clear light blue
+ case 42: return 17; // clear light green
+ case 43: return 19; // clear cyan (clear light blue)
+ case 44: return 15; // clear light red ??
+ case 45: return 11; // pink
+ case 46: return 20; // clear yellow
+ case 47: return 21; // clear white
+ case 70: return 10; // maroon (326)
+ case 78: return 13; // gold (334)
+ case 110: return 1; // orange (366 from fire logo pattern)
+ case 126: return 23;// tan (382)
+ case 127: return 27;// silver/chrome (383)
+ case 175: return 3; // mint green (431)
+ case 206: return 1; // orange (462)
+ case 238: return 6; // light yellow (494 eletric contacts)
+ case 239: return 6; // light yellow (495)
+ case 247: return 27;// 503 chrome
+ case 250: return 3; // 506 mint (Belville)
+ case 253: return 11;// 509 rose (e.g. in Paradisa)
+
+ // taken from l2p.doc but not verified
+ case 178: return 11;// 434 dark cyan (e.g. in New Technic Models)
+ case 254: return 6; // 510 light yellow (e.g. in Belville)
+ }
+ return 9; // black
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// PieceInfo construction/destruction
+
+PieceInfo::PieceInfo()
+{
+ // Not called, initialize in LoadIndex().
+}
+
+PieceInfo::~PieceInfo()
+{
+ FreeInformation();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// File I/O
+
+void PieceInfo::LoadIndex(File* file)
+{
+ short sh[6];
+ short scale;
+
+ static bool init = false;
+ if (!init)
+ {
+ for (int i = 0; i < SIDES; i++)
+ {
+ sintbl[i] = (float)sin((PI2*i)/(SIDES));
+ costbl[i] = (float)cos((PI2*i)/(SIDES));
+ }
+ init = true;
+ }
+
+
+ // TODO: don't change ref. if we're reloading ?
+ m_nRef = 0;
+ m_nVertexCount = 0;
+ m_fVertexArray = NULL;
+ m_nConnectionCount = 0;
+ m_pConnections = NULL;
+ m_nGroupCount = 0;
+ m_pGroups = NULL;
+ m_nTextureCount = 0;
+ m_pTextures = NULL;
+
+ file->Read(m_strName, 8);
+ file->Read(m_strDescription, 64);
+ file->Read(sh, sizeof(sh));
+ file->Read(&m_nFlags, sizeof(m_nFlags));
+ file->Read(&m_nGroups, sizeof(m_nGroups));
+ file->Read(&m_nOffset, sizeof(m_nOffset));
+ file->Read(&m_nSize, sizeof(m_nSize));
+
+ scale = 100;
+ if (m_nFlags & LC_PIECE_MEDIUM) scale = 1000;
+ if (m_nFlags & LC_PIECE_SMALL) scale = 10000;
+ m_fDimensions[0] = (float)sh[0]/scale;
+ m_fDimensions[1] = (float)sh[1]/scale;
+ m_fDimensions[2] = (float)sh[2]/scale;
+ m_fDimensions[3] = (float)sh[3]/scale;
+ m_fDimensions[4] = (float)sh[4]/scale;
+ m_fDimensions[5] = (float)sh[5]/scale;
+}
+
+GLuint PieceInfo::AddRef()
+{
+ if (m_nRef == 0)
+ LoadInformation();
+ m_nRef++;
+
+ for (int i = 0; i < m_nTextureCount; i++)
+ if (m_pTextures[i].texture != NULL)
+ m_pTextures[i].texture->AddRef(false);
+// TODO: get correct filter paramenter
+
+ return m_nBoxList;
+}
+
+void PieceInfo::DeRef()
+{
+ m_nRef--;
+ for (int i = 0; i < m_nTextureCount; i++)
+ if (m_pTextures[i].texture != NULL)
+ m_pTextures[i].texture->DeRef();
+
+ if (m_nRef == 0)
+ FreeInformation();
+}
+
+void PieceInfo::LoadInformation()
+{
+ FILE* bin;
+ char filename[LC_MAXPATH];
+ void* buf;
+ unsigned long verts, *longs, fixverts;
+ unsigned char *bytes, *tmp, bt;
+ unsigned short *ushorts, sh;
+ float scale, shift;
+ short* shorts;
+ CONNECTIONINFO* pConnection;
+ DRAWGROUP* pGroup;
+ int i, j;
+
+ // We don't want memory leaks.
+ FreeInformation();
+
+ // Create a display for the bounding box.
+ m_nBoxList = glGenLists(1);
+ glNewList(m_nBoxList, GL_COMPILE);
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ float box[24][3] = {
+ { m_fDimensions[0], m_fDimensions[1], m_fDimensions[2] },
+ { m_fDimensions[3], m_fDimensions[1], m_fDimensions[2] },
+ { m_fDimensions[3], m_fDimensions[4], m_fDimensions[2] },
+ { m_fDimensions[0], m_fDimensions[4], m_fDimensions[2] },
+ { m_fDimensions[0], m_fDimensions[1], m_fDimensions[5] },
+ { m_fDimensions[0], m_fDimensions[4], m_fDimensions[5] },
+ { m_fDimensions[3], m_fDimensions[4], m_fDimensions[5] },
+ { m_fDimensions[3], m_fDimensions[1], m_fDimensions[5] },
+ { m_fDimensions[3], m_fDimensions[4], m_fDimensions[2] },
+ { m_fDimensions[3], m_fDimensions[1], m_fDimensions[2] },
+ { m_fDimensions[3], m_fDimensions[1], m_fDimensions[5] },
+ { m_fDimensions[3], m_fDimensions[4], m_fDimensions[5] },
+ { m_fDimensions[0], m_fDimensions[4], m_fDimensions[5] },
+ { m_fDimensions[0], m_fDimensions[1], m_fDimensions[5] },
+ { m_fDimensions[0], m_fDimensions[1], m_fDimensions[2] },
+ { m_fDimensions[0], m_fDimensions[4], m_fDimensions[2] },
+ { m_fDimensions[0], m_fDimensions[1], m_fDimensions[5] },
+ { m_fDimensions[3], m_fDimensions[1], m_fDimensions[5] },
+ { m_fDimensions[3], m_fDimensions[1], m_fDimensions[2] },
+ { m_fDimensions[0], m_fDimensions[1], m_fDimensions[2] },
+ { m_fDimensions[0], m_fDimensions[4], m_fDimensions[2] },
+ { m_fDimensions[3], m_fDimensions[4], m_fDimensions[2] },
+ { m_fDimensions[3], m_fDimensions[4], m_fDimensions[5] },
+ { m_fDimensions[0], m_fDimensions[4], m_fDimensions[5] }
+ };
+ glVertexPointer (3, GL_FLOAT, 0, box);
+ glDrawArrays(GL_QUADS, 0, 24);
+ glEndList();
+
+ // Open pieces.bin and buffer the information we need.
+ strcpy(filename, project->GetLibraryPath());
+ strcat(filename, "pieces.bin");
+ bin = fopen(filename, "rb");
+ if (bin == NULL)
+ return;
+
+ buf = malloc(m_nSize);
+ fseek(bin, m_nOffset, SEEK_SET);
+ fread(buf, 1, m_nSize, bin);
+ fclose(bin);
+
+ shift = 1.0f/(1<<14);
+ scale = 0.01f;
+ if (m_nFlags & LC_PIECE_MEDIUM) scale = 0.001f;
+ if (m_nFlags & LC_PIECE_SMALL) scale = 0.0001f;
+ longs = (unsigned long*)buf;
+ fixverts = verts = *longs;
+ bytes = (unsigned char*)(longs + 1);
+ bytes += verts * sizeof(short) * 3;
+
+ // Read connections
+ m_nConnectionCount = *((unsigned short*)bytes);
+ bytes += sizeof(unsigned short);
+ m_pConnections = (CONNECTIONINFO*)malloc(m_nConnectionCount * sizeof(CONNECTIONINFO));
+
+ sh = m_nConnectionCount;
+ for (pConnection = m_pConnections; sh--; pConnection++)
+ {
+ pConnection->type = *bytes;
+ bytes++;
+
+ shorts = (short*)bytes;
+ pConnection->center[0] = (float)(*shorts)*scale;
+ shorts++;
+ pConnection->center[1] = (float)(*shorts)*scale;
+ shorts++;
+ pConnection->center[2] = (float)(*shorts)*scale;
+ shorts++;
+ pConnection->normal[0] = (float)(*shorts)*shift;
+ shorts++;
+ pConnection->normal[1] = (float)(*shorts)*shift;
+ shorts++;
+ pConnection->normal[2] = (float)(*shorts)*shift;
+ shorts++;
+
+ bytes = (unsigned char*)shorts;
+ }
+
+ // Load textures
+ m_nTextureCount = *bytes;
+ if (m_nTextureCount > 0)
+ m_pTextures = (TEXTURE*)malloc(m_nTextureCount*sizeof(TEXTURE));
+ bytes++;
+
+ for (sh = 0; sh < m_nTextureCount; sh++)
+ {
+ char name[9];
+ TEXTURE* tex = &m_pTextures[sh];
+ tex->color = ConvertColor(*bytes);
+ bytes++;
+
+ strcpy(name, (char*)bytes);
+ tex->texture = project->FindTexture(name);
+
+ shorts = (short*)(bytes + 8);
+ for (i = 0; i < 4; i++)
+ {
+ tex->vertex[i][0] = (float)shorts[0]*scale;
+ tex->vertex[i][1] = (float)shorts[1]*scale;
+ tex->vertex[i][2] = (float)shorts[2]*scale;
+ shorts += 3;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ tex->coords[i][0] = (float)shorts[0];
+ tex->coords[i][1] = (float)shorts[1];
+ shorts += 2;
+ }
+
+ bytes += 8 + 20*sizeof(unsigned short);
+ }
+
+ // Read groups
+ m_nGroupCount = *((unsigned short*)bytes);
+ bytes += sizeof(unsigned short);
+ m_pGroups = (DRAWGROUP*)malloc(sizeof(DRAWGROUP)*m_nGroupCount);
+ memset(m_pGroups, 0, sizeof(DRAWGROUP)*m_nGroupCount);
+
+ // First we need to know the number of vertexes
+ tmp = bytes;
+ sh = m_nGroupCount;
+ unsigned long quads = 0;
+ while (sh--)
+ {
+ bt = *bytes;
+ bytes++;
+ bytes += bt*sizeof(unsigned short);
+
+ while (*bytes)
+ {
+ if (*bytes == LC_MESH)
+ {
+ if (fixverts > 65535)
+ {
+ unsigned long colors, *p;
+ p = (unsigned long*)(bytes + 1);
+ colors = *p;
+ p++;
+
+ while (colors--)
+ {
+ p++; // color code
+ quads += *p;
+ p += *p + 1;
+ p += *p + 1;
+ p += *p + 1;
+ }
+
+ bytes = (unsigned char*)p;
+ }
+ else
+ {
+ unsigned short colors, *p;
+ p = (unsigned short*)(bytes + 1);
+ colors = *p;
+ p++;
+
+ while (colors--)
+ {
+ p++; // color code
+ quads += *p;
+ p += *p + 1;
+ p += *p + 1;
+ p += *p + 1;
+ }
+
+ bytes = (unsigned char*)p;
+ }
+ }
+
+ if (*bytes == LC_STUD)
+ {
+ verts += (2*SIDES)+1;
+ quads += 4*SIDES;
+ bytes += 2*sizeof(unsigned char) + 12*sizeof(float);
+ }
+
+ if (*bytes == LC_STUD2)
+ {
+ verts += 4*SIDES;
+ quads += 12*SIDES;
+ bytes += 2*sizeof(unsigned char) + 12*sizeof(float);
+ }
+
+ if (*bytes == LC_STUD3)
+ {
+ verts += (2*SIDES)+1;
+ quads += 4*SIDES;
+ bytes += 2*sizeof(unsigned char) + 12*sizeof(float);
+ }
+
+ if (*bytes == LC_STUD4)
+ {
+ verts += 4*SIDES;
+ quads += 12*SIDES;
+ bytes += 2*sizeof(unsigned char) + 12*sizeof(float);
+ }
+ }
+ bytes++; // should be 0
+ }
+
+ m_fVertexArray = (float*)malloc(3*sizeof(float)*verts);
+ m_nVertexCount = verts;
+ if ((verts > 65535) || (quads > 65535))
+ m_nFlags |= LC_PIECE_LONGDATA;
+ else
+ m_nFlags &= ~LC_PIECE_LONGDATA;
+
+ // Copy the 'fixed' vertexes
+ shorts = (short*)(longs + 1);
+ for (verts = 0; verts < *longs; verts++)
+ {
+ m_fVertexArray[verts*3] = (float)(*shorts)*scale;
+ shorts++;
+ m_fVertexArray[verts*3+1] = (float)(*shorts)*scale;
+ shorts++;
+ m_fVertexArray[verts*3+2] = (float)(*shorts)*scale;
+ shorts++;
+ }
+
+ // Read groups
+ bytes = tmp;
+ sh = m_nGroupCount;
+ for (pGroup = m_pGroups; sh--; pGroup++)
+ {
+ bt = *bytes;
+ bytes++;
+
+ pGroup->connections[bt] = 0xFFFF;
+ while(bt--)
+ {
+ unsigned short tmp = *bytes;
+ pGroup->connections[bt] = tmp;
+ bytes += sizeof(unsigned short);
+ }
+
+ // Currently there's only one type of drawinfo (mesh or stud)
+ // per group but this will change in the future.
+ switch (*bytes)
+ {
+ case LC_MESH:
+ if (fixverts > 65535)
+ {
+ unsigned long colors, *p;
+ bytes++;
+ p = (unsigned long*)bytes;
+ colors = *p;
+ p++;
+
+ while (colors--)
+ {
+ *p = ConvertColor(*p);
+ p++; // color code
+ p += *p + 1;
+ p += *p + 1;
+ p += *p + 1;
+ }
+
+ i = (unsigned char*)p - bytes;
+ pGroup->drawinfo = malloc(i);
+ memcpy(pGroup->drawinfo, bytes, i);
+ bytes = (unsigned char*)p;
+ }
+ else
+ {
+ unsigned short colors, *p;
+ bytes++;
+ p = (unsigned short*)bytes;
+ colors = *p;
+ p++;
+
+ while (colors--)
+ {
+ *p = ConvertColor(*p);
+ p++; // color code
+ p += *p + 1;
+ p += *p + 1;
+ p += *p + 1;
+ }
+
+ i = (unsigned char*)p - bytes;
+
+ if (m_nFlags & LC_PIECE_LONGDATA)
+ {
+ pGroup->drawinfo = malloc(i*sizeof(unsigned long)/sizeof(unsigned short));
+ longs = (unsigned long*)pGroup->drawinfo;
+
+ for (ushorts = (unsigned short*)bytes; ushorts != p; ushorts++, longs++)
+ *longs = *ushorts;
+ }
+ else
+ {
+ pGroup->drawinfo = malloc(i);
+ memcpy(pGroup->drawinfo, bytes, i);
+ }
+
+ bytes = (unsigned char*)p;
+ }
+ break;
+
+ case LC_STUD:
+ {
+ int size;
+ Matrix mat((float*)(bytes+2));
+ unsigned short color = ConvertColor(*(bytes+1));
+
+ // Create the vertexes
+ for (i = 0; i < SIDES; i++)
+ {
+ m_fVertexArray[(verts+i+SIDES)*3] =
+ m_fVertexArray[(verts+i)*3] =
+ LC_STUD_RADIUS * costbl[i];
+ m_fVertexArray[(verts+i+SIDES)*3+1] =
+ m_fVertexArray[(verts+i)*3+1] =
+ LC_STUD_RADIUS * sintbl[i];
+ m_fVertexArray[(verts+i)*3+2] = 0;
+ m_fVertexArray[(verts+i+SIDES)*3+2] = LC_STUD_HEIGHT;
+ }
+ m_fVertexArray[(verts+2*SIDES)*3] = 0;
+ m_fVertexArray[(verts+2*SIDES)*3+1] = 0;
+ m_fVertexArray[(verts+2*SIDES)*3+2] = LC_STUD_HEIGHT;
+
+ mat.TransformPoints(&m_fVertexArray[verts*3], 2*SIDES+1);
+ // colors + 2*num_prim + sides*prims
+ size = 9+SIDES*11;
+
+ if (m_nFlags & LC_PIECE_LONGDATA)
+ {
+ pGroup->drawinfo = malloc(sizeof(unsigned long)*size);
+ longs = (unsigned long*)pGroup->drawinfo;
+
+ longs[0] = 2; // colors
+ longs[1] = color;
+ longs[2] = SIDES*4;
+ j = 3;
+
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[3+i*4] = (unsigned long)verts + i;
+ if (i == SIDES-1)
+ {
+ longs[4+i*4] = (unsigned long)verts;
+ longs[5+i*4] = (unsigned long)verts + SIDES;
+ }
+ else
+ {
+ longs[4+i*4] = (unsigned long)verts + i + 1;
+ longs[5+i*4] = (unsigned long)verts + SIDES + i + 1;
+ }
+ longs[6+i*4] = (unsigned long)verts + SIDES + i;
+ }
+ j += 4*SIDES;
+ longs[j] = SIDES*3;
+ j++;
+
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[j+i*3] = (unsigned short)verts + 2*SIDES;
+ longs[1+j+i*3] = (unsigned short)verts + SIDES + i;
+ if (i == SIDES-1)
+ longs[2+j+i*3] = (unsigned short)verts + SIDES;
+ else
+ longs[2+j+i*3] = (unsigned short)verts + SIDES + i + 1;
+ }
+
+ j += 3*SIDES;
+ longs[j] = 0; j++; // lines
+ longs[j] = LC_COL_EDGES; j++; // color
+ longs[j] = 0; j++; // quads
+ longs[j] = 0; j++; // tris
+ longs[j] = 4*SIDES; j++;
+
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[j+i*4] = (unsigned long)verts + i;
+ if (i == SIDES-1)
+ longs[1+j+i*4] = (unsigned long)verts;
+ else
+ longs[1+j+i*4] = (unsigned long)verts + i + 1;
+
+ longs[2+j+i*4] = longs[j+i*4] + SIDES;
+ longs[3+j+i*4] = longs[1+j+i*4] + SIDES;
+ }
+ }
+ else
+ {
+ pGroup->drawinfo = malloc(sizeof(unsigned short)*size);
+ ushorts = (unsigned short*)pGroup->drawinfo;
+
+ ushorts[0] = 2; // colors
+ ushorts[1] = color;
+ ushorts[2] = SIDES*4;
+ j = 3;
+
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[3+i*4] = (unsigned short)(verts + i);
+ if (i == SIDES-1)
+ {
+ ushorts[4+i*4] = (unsigned short)verts;
+ ushorts[5+i*4] = (unsigned short)verts + SIDES;
+ }
+ else
+ {
+ ushorts[4+i*4] = (unsigned short)verts + i + 1;
+ ushorts[5+i*4] = (unsigned short)verts + SIDES + i + 1;
+ }
+ ushorts[6+i*4] = (unsigned short)verts + SIDES + i;
+ }
+ j += 4*SIDES;
+ ushorts[j] = SIDES*3;
+ j++;
+
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[j+i*3] = (unsigned short)verts + 2*SIDES;
+ ushorts[1+j+i*3] = (unsigned short)verts + SIDES + i;
+ if (i == SIDES-1)
+ ushorts[2+j+i*3] = (unsigned short)verts + SIDES;
+ else
+ ushorts[2+j+i*3] = (unsigned short)verts + SIDES + i + 1;
+ }
+
+ j += 3*SIDES;
+ ushorts[j] = 0; j++; // lines
+ ushorts[j] = LC_COL_EDGES; j++; // color
+ ushorts[j] = 0; j++; // quads
+ ushorts[j] = 0; j++; // tris
+ ushorts[j] = 4*SIDES; j++;
+
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[j+i*4] = (unsigned short)verts + i;
+ if (i == SIDES-1)
+ ushorts[1+j+i*4] = (unsigned short)verts;
+ else
+ ushorts[1+j+i*4] = (unsigned short)verts + i + 1;
+
+ ushorts[2+j+i*4] = ushorts[j+i*4] + SIDES;
+ ushorts[3+j+i*4] = ushorts[1+j+i*4] + SIDES;
+ }
+ }
+
+ verts += 2*SIDES+1;
+ bytes += 2*sizeof(unsigned char) + 12*sizeof(float);
+ } break;
+
+ case LC_STUD2:
+ {
+ int size;
+ Matrix mat((float*)(bytes+2));
+ unsigned short color = ConvertColor(*(bytes+1));
+
+ // Create the vertexes
+ for (i = 0; i < SIDES; i++)
+ {
+ // outside
+ m_fVertexArray[(verts+i+SIDES)*3] =
+ m_fVertexArray[(verts+i)*3] =
+ LC_STUD_RADIUS * costbl[i];
+ m_fVertexArray[(verts+i+SIDES)*3+1] =
+ m_fVertexArray[(verts+i)*3+1] =
+ LC_STUD_RADIUS * sintbl[i];
+ m_fVertexArray[(verts+i)*3+2] = LC_STUD_HEIGHT;
+ m_fVertexArray[(verts+i+SIDES)*3+2] = 0;
+
+ // inside
+ m_fVertexArray[(verts+i+2*SIDES)*3] =
+ m_fVertexArray[(verts+i+3*SIDES)*3] =
+ 0.16f * costbl[i];
+ m_fVertexArray[(verts+i+2*SIDES)*3+1] =
+ m_fVertexArray[(verts+i+3*SIDES)*3+1] =
+ 0.16f * sintbl[i];
+ m_fVertexArray[(verts+i+3*SIDES)*3+2] = LC_STUD_HEIGHT;
+ m_fVertexArray[(verts+i+2*SIDES)*3+2] = 0;
+ }
+
+ mat.TransformPoints(&m_fVertexArray[verts*3], 4*SIDES);
+ // colors + 2*num_prim + sides*prims
+ size = 9+SIDES*20;
+
+ if (m_nFlags & LC_PIECE_LONGDATA)
+ {
+ pGroup->drawinfo = malloc(sizeof(unsigned long)*size);
+ longs = (unsigned long*)pGroup->drawinfo;
+
+ longs[0] = 2; // colors
+ longs[1] = color;
+ longs[2] = SIDES*12;
+ j = 3;
+
+ // outside
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[j+i*4] = (unsigned long)(verts + SIDES + i);
+ if (i == SIDES-1)
+ {
+ longs[j+1+i*4] = (unsigned long)verts + SIDES;
+ longs[j+2+i*4] = (unsigned long)verts;
+ }
+ else
+ {
+ longs[j+1+i*4] = (unsigned long)verts + SIDES + i + 1;
+ longs[j+2+i*4] = (unsigned long)verts + i + 1;
+ }
+ longs[j+3+i*4] = (unsigned long)verts + i;
+ }
+ j += 4*SIDES;
+
+ // inside
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[j+i*4] = (unsigned long)(verts + 2*SIDES + i);
+ if (i == SIDES-1)
+ {
+ longs[j+1+i*4] = (unsigned long)verts + 2*SIDES;
+ longs[j+2+i*4] = (unsigned long)verts + 3*SIDES;
+ }
+ else
+ {
+ longs[j+1+i*4] = (unsigned long)verts + 2*SIDES + i + 1;
+ longs[j+2+i*4] = (unsigned long)verts + 3*SIDES + i + 1;
+ }
+ longs[j+3+i*4] = (unsigned long)verts + 3*SIDES + i;
+ }
+ j += 4*SIDES;
+
+ // ring
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[j+i*4] = (unsigned long)(verts + i);
+ if (i == SIDES-1)
+ {
+ longs[j+1+i*4] = (unsigned long)verts;
+ longs[j+2+i*4] = (unsigned long)verts + 3*SIDES;
+ }
+ else
+ {
+ longs[j+1+i*4] = (unsigned long)verts + i + 1;
+ longs[j+2+i*4] = (unsigned long)verts + 3*SIDES + i + 1;
+ }
+ longs[j+3+i*4] = (unsigned long)verts + 3*SIDES + i;
+ }
+ j += 4*SIDES;
+
+ longs[j] = 0; j++; // tris
+ longs[j] = 0; j++; // lines
+ longs[j] = LC_COL_EDGES; j++; // color
+ longs[j] = 0; j++; // quads
+ longs[j] = 0; j++; // tris
+ longs[j] = 8*SIDES; j++;
+
+ // outside
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[j+i*4] = (unsigned long)verts + i;
+ if (i == SIDES-1)
+ longs[1+j+i*4] = (unsigned long)verts;
+ else
+ longs[1+j+i*4] = (unsigned long)verts + i + 1;
+
+ longs[2+j+i*4] = longs[j+i*4] + SIDES;
+ longs[3+j+i*4] = longs[1+j+i*4] + SIDES;
+ }
+ j += 4*SIDES;
+
+ // inside
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[j+i*4] = (unsigned long)verts + 2*SIDES + i;
+ if (i == SIDES-1)
+ longs[1+j+i*4] = (unsigned long)verts + 2*SIDES;
+ else
+ longs[1+j+i*4] = (unsigned long)verts + 2*SIDES + i + 1;
+
+ longs[2+j+i*4] = longs[j+i*4] + SIDES;
+ longs[3+j+i*4] = longs[1+j+i*4] + SIDES;
+ }
+ }
+ else
+ {
+ pGroup->drawinfo = malloc(sizeof(unsigned short)*size);
+ ushorts = (unsigned short*)pGroup->drawinfo;
+
+ ushorts[0] = 2; // colors
+ ushorts[1] = color;
+ ushorts[2] = SIDES*12;
+ j = 3;
+
+ // outside
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[j+i*4] = (unsigned short)(verts + SIDES + i);
+ if (i == SIDES-1)
+ {
+ ushorts[j+1+i*4] = (unsigned short)verts + SIDES;
+ ushorts[j+2+i*4] = (unsigned short)verts;
+ }
+ else
+ {
+ ushorts[j+1+i*4] = (unsigned short)verts + SIDES + i + 1;
+ ushorts[j+2+i*4] = (unsigned short)verts + i + 1;
+ }
+ ushorts[j+3+i*4] = (unsigned short)verts + i;
+ }
+ j += 4*SIDES;
+
+ // inside
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[j+i*4] = (unsigned short)(verts + 3*SIDES + i);
+ if (i == SIDES-1)
+ {
+ ushorts[j+1+i*4] = (unsigned short)verts + 3*SIDES;
+ ushorts[j+2+i*4] = (unsigned short)verts + 2*SIDES;
+ }
+ else
+ {
+ ushorts[j+1+i*4] = (unsigned short)verts + 3*SIDES + i + 1;
+ ushorts[j+2+i*4] = (unsigned short)verts + 2*SIDES + i + 1;
+ }
+ ushorts[j+3+i*4] = (unsigned short)verts + 2*SIDES + i;
+ }
+ j += 4*SIDES;
+
+ // ring
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[j+i*4] = (unsigned short)(verts + i);
+ if (i == SIDES-1)
+ {
+ ushorts[j+1+i*4] = (unsigned short)verts;
+ ushorts[j+2+i*4] = (unsigned short)verts + 3*SIDES;
+ }
+ else
+ {
+ ushorts[j+1+i*4] = (unsigned short)verts + i + 1;
+ ushorts[j+2+i*4] = (unsigned short)verts + 3*SIDES + i + 1;
+ }
+ ushorts[j+3+i*4] = (unsigned short)verts + 3*SIDES + i;
+ }
+ j += 4*SIDES;
+
+ ushorts[j] = 0; j++; // tris
+ ushorts[j] = 0; j++; // lines
+ ushorts[j] = LC_COL_EDGES; j++; // color
+ ushorts[j] = 0; j++; // quads
+ ushorts[j] = 0; j++; // tris
+ ushorts[j] = 8*SIDES; j++;
+
+ // outside
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[j+i*4] = (unsigned short)verts + i;
+ if (i == SIDES-1)
+ ushorts[1+j+i*4] = (unsigned short)verts;
+ else
+ ushorts[1+j+i*4] = (unsigned short)verts + i + 1;
+
+ ushorts[2+j+i*4] = ushorts[j+i*4] + SIDES;
+ ushorts[3+j+i*4] = ushorts[1+j+i*4] + SIDES;
+ }
+ j += 4*SIDES;
+
+ // inside
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[j+i*4] = (unsigned short)verts + 2*SIDES + i;
+ if (i == SIDES-1)
+ ushorts[1+j+i*4] = (unsigned short)verts + 2*SIDES;
+ else
+ ushorts[1+j+i*4] = (unsigned short)verts + 2*SIDES + i + 1;
+
+ ushorts[2+j+i*4] = ushorts[j+i*4] + SIDES;
+ ushorts[3+j+i*4] = ushorts[1+j+i*4] + SIDES;
+ }
+ }
+
+ verts += 4*SIDES;
+ bytes += 2*sizeof(unsigned char) + 12*sizeof(float);
+ } break;
+
+ case LC_STUD3:
+ {
+ int size;
+ Matrix mat((float*)(bytes+2));
+ unsigned short color = ConvertColor(*(bytes+1));
+
+ // Create the vertexes
+ for (i = 0; i < SIDES; i++)
+ {
+ m_fVertexArray[(verts+i+SIDES)*3] =
+ m_fVertexArray[(verts+i)*3] =
+ 0.16f * costbl[i];
+ m_fVertexArray[(verts+i+SIDES)*3+1] =
+ m_fVertexArray[(verts+i)*3+1] =
+ 0.16f * sintbl[i];
+ m_fVertexArray[(verts+i)*3+2] = 0;
+ m_fVertexArray[(verts+i+SIDES)*3+2] = LC_STUD_HEIGHT;
+ }
+ m_fVertexArray[(verts+2*SIDES)*3] = 0;
+ m_fVertexArray[(verts+2*SIDES)*3+1] = 0;
+ m_fVertexArray[(verts+2*SIDES)*3+2] = LC_STUD_HEIGHT;
+
+ mat.TransformPoints(&m_fVertexArray[verts*3], 2*SIDES+1);
+ // colors + 2*num_prim + sides*prims
+ size = 9+SIDES*11;
+
+ if (m_nFlags & LC_PIECE_LONGDATA)
+ {
+ pGroup->drawinfo = malloc(sizeof(unsigned long)*size);
+ longs = (unsigned long*)pGroup->drawinfo;
+
+ longs[0] = 2; // colors
+ longs[1] = color;
+ longs[2] = SIDES*4;
+ j = 3;
+
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[3+i*4] = (unsigned long)verts + SIDES + i;
+ if (i == SIDES-1)
+ {
+ longs[4+i*4] = (unsigned long)verts + SIDES;
+ longs[5+i*4] = (unsigned long)verts;
+ }
+ else
+ {
+ longs[4+i*4] = (unsigned long)verts + SIDES + i + 1;
+ longs[5+i*4] = (unsigned long)verts + i + 1;
+ }
+ longs[6+i*4] = (unsigned long)verts + i;
+ }
+ j += 4*SIDES;
+ longs[j] = SIDES*3;
+ j++;
+
+ for (i = 0; i < SIDES; i++)
+ {
+ if (i == SIDES-1)
+ longs[j+i*3] = (unsigned short)verts + SIDES;
+ else
+ longs[j+i*3] = (unsigned short)verts + SIDES + i + 1;
+ longs[1+j+i*3] = (unsigned short)verts + SIDES + i;
+ longs[2+j+i*3] = (unsigned short)verts + 2*SIDES;
+ }
+
+ j += 3*SIDES;
+ longs[j] = 0; j++; // lines
+ longs[j] = LC_COL_EDGES; j++; // color
+ longs[j] = 0; j++; // quads
+ longs[j] = 0; j++; // tris
+ longs[j] = 4*SIDES; j++;
+
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[j+i*4] = (unsigned long)verts + i;
+ if (i == SIDES-1)
+ longs[1+j+i*4] = (unsigned long)verts;
+ else
+ longs[1+j+i*4] = (unsigned long)verts + i + 1;
+
+ longs[2+j+i*4] = longs[j+i*4] + SIDES;
+ longs[3+j+i*4] = longs[1+j+i*4] + SIDES;
+ }
+ }
+ else
+ {
+ pGroup->drawinfo = malloc(sizeof(unsigned short)*size);
+ ushorts = (unsigned short*)pGroup->drawinfo;
+
+ ushorts[0] = 2; // colors
+ ushorts[1] = color;
+ ushorts[2] = SIDES*4;
+ j = 3;
+
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[3+i*4] = (unsigned short)(verts + SIDES + i);
+ if (i == SIDES-1)
+ {
+ ushorts[4+i*4] = (unsigned short)verts + SIDES;
+ ushorts[5+i*4] = (unsigned short)verts;
+ }
+ else
+ {
+ ushorts[4+i*4] = (unsigned short)verts + SIDES + i + 1;
+ ushorts[5+i*4] = (unsigned short)verts + i + 1;
+ }
+ ushorts[6+i*4] = (unsigned short)verts + i;
+ }
+ j += 4*SIDES;
+ ushorts[j] = SIDES*3;
+ j++;
+
+ for (i = 0; i < SIDES; i++)
+ {
+ if (i == SIDES-1)
+ ushorts[j+i*3] = (unsigned short)verts + SIDES;
+ else
+ ushorts[j+i*3] = (unsigned short)verts + SIDES + i + 1;
+ ushorts[1+j+i*3] = (unsigned short)verts + SIDES + i;
+ ushorts[2+j+i*3] = (unsigned short)verts + 2*SIDES;
+ }
+
+ j += 3*SIDES;
+ ushorts[j] = 0; j++; // lines
+ ushorts[j] = LC_COL_EDGES; j++; // color
+ ushorts[j] = 0; j++; // quads
+ ushorts[j] = 0; j++; // tris
+ ushorts[j] = 4*SIDES; j++;
+
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[j+i*4] = (unsigned short)verts + i;
+ if (i == SIDES-1)
+ ushorts[1+j+i*4] = (unsigned short)verts;
+ else
+ ushorts[1+j+i*4] = (unsigned short)verts + i + 1;
+
+ ushorts[2+j+i*4] = ushorts[j+i*4] + SIDES;
+ ushorts[3+j+i*4] = ushorts[1+j+i*4] + SIDES;
+ }
+ }
+
+ verts += 2*SIDES+1;
+ bytes += 2*sizeof(unsigned char) + 12*sizeof(float);
+ } break;
+
+ case LC_STUD4:
+ {
+ int size;
+ Matrix mat((float*)(bytes+2));
+ unsigned short color = ConvertColor(*(bytes+1));
+
+ // Create the vertexes
+ for (i = 0; i < SIDES; i++)
+ {
+ // outside
+ m_fVertexArray[(verts+i+SIDES)*3] =
+ m_fVertexArray[(verts+i)*3] =
+ LC_KNOB_RADIUS * costbl[i];
+ m_fVertexArray[(verts+i+SIDES)*3+1] =
+ m_fVertexArray[(verts+i)*3+1] =
+ LC_KNOB_RADIUS * sintbl[i];
+ m_fVertexArray[(verts+i)*3+2] = LC_STUD_HEIGHT;
+ m_fVertexArray[(verts+i+SIDES)*3+2] = 0;
+
+ // inside
+ m_fVertexArray[(verts+i+2*SIDES)*3] =
+ m_fVertexArray[(verts+i+3*SIDES)*3] =
+ LC_STUD_RADIUS * costbl[i];
+ m_fVertexArray[(verts+i+2*SIDES)*3+1] =
+ m_fVertexArray[(verts+i+3*SIDES)*3+1] =
+ LC_STUD_RADIUS * sintbl[i];
+ m_fVertexArray[(verts+i+3*SIDES)*3+2] = LC_STUD_HEIGHT;
+ m_fVertexArray[(verts+i+2*SIDES)*3+2] = 0;
+ }
+
+ mat.TransformPoints(&m_fVertexArray[verts*3], 4*SIDES);
+ // colors + 2*num_prim + sides*prims
+ size = 9+SIDES*20;
+
+ if (m_nFlags & LC_PIECE_LONGDATA)
+ {
+ pGroup->drawinfo = malloc(sizeof(unsigned long)*size);
+ longs = (unsigned long*)pGroup->drawinfo;
+
+ longs[0] = 2; // colors
+ longs[1] = color;
+ longs[2] = SIDES*12;
+ j = 3;
+
+ // outside
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[j+i*4] = (unsigned long)(verts + i);
+ if (i == SIDES-1)
+ {
+ longs[j+1+i*4] = (unsigned long)verts;
+ longs[j+2+i*4] = (unsigned long)verts + SIDES;
+ }
+ else
+ {
+ longs[j+1+i*4] = (unsigned long)verts + i + 1;
+ longs[j+2+i*4] = (unsigned long)verts + SIDES + i + 1;
+ }
+ longs[j+3+i*4] = (unsigned long)verts + SIDES + i;
+ }
+ j += 4*SIDES;
+
+ // inside
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[j+i*4] = (unsigned long)(verts + 3*SIDES + i);
+ if (i == SIDES-1)
+ {
+ longs[j+1+i*4] = (unsigned long)verts + 3*SIDES;
+ longs[j+2+i*4] = (unsigned long)verts + 2*SIDES;
+ }
+ else
+ {
+ longs[j+1+i*4] = (unsigned long)verts + 3*SIDES + i + 1;
+ longs[j+2+i*4] = (unsigned long)verts + 2*SIDES + i + 1;
+ }
+ longs[j+3+i*4] = (unsigned long)verts + 2*SIDES + i;
+ }
+ j += 4*SIDES;
+
+ // ring
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[j+i*4] = (unsigned long)(verts + 3*SIDES + i);
+ if (i == SIDES-1)
+ {
+ longs[j+1+i*4] = (unsigned long)verts + 3*SIDES;
+ longs[j+2+i*4] = (unsigned long)verts;
+ }
+ else
+ {
+ longs[j+1+i*4] = (unsigned long)verts + 3*SIDES + i + 1;
+ longs[j+2+i*4] = (unsigned long)verts + i + 1;
+ }
+ longs[j+3+i*4] = (unsigned long)verts + i;
+ }
+ j += 4*SIDES;
+
+ longs[j] = 0; j++; // tris
+ longs[j] = 0; j++; // lines
+ longs[j] = LC_COL_EDGES; j++; // color
+ longs[j] = 0; j++; // quads
+ longs[j] = 0; j++; // tris
+ longs[j] = 8*SIDES; j++;
+
+ // outside
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[j+i*4] = (unsigned long)verts + i;
+ if (i == SIDES-1)
+ longs[1+j+i*4] = (unsigned long)verts;
+ else
+ longs[1+j+i*4] = (unsigned long)verts + i + 1;
+
+ longs[2+j+i*4] = longs[j+i*4] + SIDES;
+ longs[3+j+i*4] = longs[1+j+i*4] + SIDES;
+ }
+ j += 4*SIDES;
+
+ // inside
+ for (i = 0; i < SIDES; i++)
+ {
+ longs[j+i*4] = (unsigned long)verts + 2*SIDES + i;
+ if (i == SIDES-1)
+ longs[1+j+i*4] = (unsigned long)verts + 2*SIDES;
+ else
+ longs[1+j+i*4] = (unsigned long)verts + 2*SIDES + i + 1;
+
+ longs[2+j+i*4] = longs[j+i*4] + SIDES;
+ longs[3+j+i*4] = longs[1+j+i*4] + SIDES;
+ }
+ }
+ else
+ {
+ pGroup->drawinfo = malloc(sizeof(unsigned short)*size);
+ ushorts = (unsigned short*)pGroup->drawinfo;
+
+ ushorts[0] = 2; // colors
+ ushorts[1] = color;
+ ushorts[2] = SIDES*12;
+ j = 3;
+
+ // outside
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[j+i*4] = (unsigned short)(verts + i);
+ if (i == SIDES-1)
+ {
+ ushorts[j+1+i*4] = (unsigned short)verts;
+ ushorts[j+2+i*4] = (unsigned short)verts + SIDES;
+ }
+ else
+ {
+ ushorts[j+1+i*4] = (unsigned short)verts + i + 1;
+ ushorts[j+2+i*4] = (unsigned short)verts + SIDES + i + 1;
+ }
+ ushorts[j+3+i*4] = (unsigned short)verts + SIDES + i;
+ }
+ j += 4*SIDES;
+
+ // inside
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[j+i*4] = (unsigned short)(verts + 2*SIDES + i);
+ if (i == SIDES-1)
+ {
+ ushorts[j+1+i*4] = (unsigned short)verts + 2*SIDES;
+ ushorts[j+2+i*4] = (unsigned short)verts + 3*SIDES;
+ }
+ else
+ {
+ ushorts[j+1+i*4] = (unsigned short)verts + 2*SIDES + i + 1;
+ ushorts[j+2+i*4] = (unsigned short)verts + 3*SIDES + i + 1;
+ }
+ ushorts[j+3+i*4] = (unsigned short)verts + 3*SIDES + i;
+ }
+ j += 4*SIDES;
+
+ // ring
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[j+i*4] = (unsigned short)(verts + 3*SIDES + i);
+ if (i == SIDES-1)
+ {
+ ushorts[j+1+i*4] = (unsigned short)verts + 3*SIDES;
+ ushorts[j+2+i*4] = (unsigned short)verts;
+ }
+ else
+ {
+ ushorts[j+1+i*4] = (unsigned short)verts + 3*SIDES + i + 1;
+ ushorts[j+2+i*4] = (unsigned short)verts + i + 1;
+ }
+ ushorts[j+3+i*4] = (unsigned short)verts + i;
+ }
+ j += 4*SIDES;
+
+ ushorts[j] = 0; j++; // tris
+ ushorts[j] = 0; j++; // lines
+ ushorts[j] = LC_COL_EDGES; j++; // color
+ ushorts[j] = 0; j++; // quads
+ ushorts[j] = 0; j++; // tris
+ ushorts[j] = 8*SIDES; j++;
+
+ // outside
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[j+i*4] = (unsigned short)verts + i;
+ if (i == SIDES-1)
+ ushorts[1+j+i*4] = (unsigned short)verts;
+ else
+ ushorts[1+j+i*4] = (unsigned short)verts + i + 1;
+
+ ushorts[2+j+i*4] = ushorts[j+i*4] + SIDES;
+ ushorts[3+j+i*4] = ushorts[1+j+i*4] + SIDES;
+ }
+ j += 4*SIDES;
+
+ // inside
+ for (i = 0; i < SIDES; i++)
+ {
+ ushorts[j+i*4] = (unsigned short)verts + 2*SIDES + i;
+ if (i == SIDES-1)
+ ushorts[1+j+i*4] = (unsigned short)verts + 2*SIDES;
+ else
+ ushorts[1+j+i*4] = (unsigned short)verts + 2*SIDES + i + 1;
+
+ ushorts[2+j+i*4] = ushorts[j+i*4] + SIDES;
+ ushorts[3+j+i*4] = ushorts[1+j+i*4] + SIDES;
+ }
+ }
+
+ verts += 4*SIDES;
+ bytes += 2*sizeof(unsigned char) + 12*sizeof(float);
+ } break;
+ }
+ bytes++; // should be 0
+ }
+
+ free(buf);
+
+/*
+ // Now create the information for the CD
+ // If the object is big this can block the program for serveral seconds.
+ // ATTENTION: The RAPID CD library is based on triangles.
+
+ if (pInfo->pRModel)
+ delete pInfo->pRModel;
+
+ pInfo->pRModel = new CRModel();
+ pInfo->pRModel->BeginModel();
+
+ UINT col, loc, j, i;
+ int vert = 0;
+
+ for (UINT c = 0; c < pInfo->cons; c++)
+ {
+ if (pInfo->connection[c].info == NULL)
+ continue;
+ if (pInfo->count > 65535)
+ {
+ UINT* info = (UINT*)pInfo->connection[c].info;
+ loc = 1;
+ col = info[0];
+ while (col)
+ {
+ loc++;
+
+ j = info[loc];
+ for (i = 0; i < j; i+=4)
+ {
+ pInfo->pRModel->AddTri(&pInfo->vertex[info[loc+i+1]*3], &pInfo->vertex[info[loc+i+2]*3],
+ &pInfo->vertex[info[loc+i+3]*3], vert);
+ vert++;
+ pInfo->pRModel->AddTri(&pInfo->vertex[info[loc+i+3]*3], &pInfo->vertex[info[loc+i+4]*3],
+ &pInfo->vertex[info[loc+i+1]*3], vert);
+ vert++;
+ }
+ loc += j+1;
+ j = info[loc];
+ for (i = 0; i < j; i+=3)
+ {
+ pInfo->pRModel->AddTri(&pInfo->vertex[info[loc+i+1]*3], &pInfo->vertex[info[loc+i+2]*3],
+ &pInfo->vertex[info[loc+i+3]*3], vert);
+ vert++;
+ }
+ loc += j+1;
+ loc += info[loc]+1;
+
+ col--;
+ }
+ }
+ else
+ {
+ WORD* info = (WORD*)pInfo->connection[c].info;
+ loc = 1;
+ col = info[0];
+ while (col)
+ {
+ loc++;
+
+ j = info[loc];
+ for (i = 0; i < j; i+=4)
+ {
+ pInfo->pRModel->AddTri(&pInfo->vertex[info[loc+i+1]*3], &pInfo->vertex[info[loc+i+2]*3],
+ &pInfo->vertex[info[loc+i+3]*3], vert);
+ vert++;
+ pInfo->pRModel->AddTri(&pInfo->vertex[info[loc+i+3]*3], &pInfo->vertex[info[loc+i+4]*3],
+ &pInfo->vertex[info[loc+i+1]*3], vert);
+ vert++;
+ }
+ loc += j+1;
+ j = info[loc];
+ for (i = 0; i < j; i+=3)
+ {
+ pInfo->pRModel->AddTri(&pInfo->vertex[info[loc+i+1]*3], &pInfo->vertex[info[loc+i+2]*3],
+ &pInfo->vertex[info[loc+i+3]*3], vert);
+ vert++;
+ }
+ loc += j+1;
+ loc += info[loc]+1;
+
+ col--;
+ }
+ }
+ }
+ pInfo->pRModel->EndModel();
+*/
+}
+
+void PieceInfo::FreeInformation()
+{
+ if (m_nBoxList != 0)
+ glDeleteLists(m_nBoxList, 1);
+ m_nBoxList = 0;
+
+ if (m_fVertexArray != NULL)
+ {
+ free(m_fVertexArray);
+ m_fVertexArray = NULL;
+ m_nVertexCount = 0;
+ }
+
+ if (m_pConnections != NULL)
+ {
+ free(m_pConnections);
+ m_pConnections = NULL;
+ m_nConnectionCount = 0;
+ }
+
+ if (m_pGroups != NULL)
+ {
+ while (m_nGroupCount--)
+ if (m_pGroups[m_nGroupCount].drawinfo)
+ free(m_pGroups[m_nGroupCount].drawinfo);
+
+ free(m_pGroups);
+ m_pGroups = NULL;
+ }
+
+ if (m_pTextures != NULL)
+ {
+// while (m_nTextureCount--)
+// if (m_pTextures[m_nTextureCount].texture)
+// m_pTextures[m_nTextureCount].texture->DeRef();
+
+ free(m_pTextures);
+ m_pTextures = NULL;
+ }
+}
+
+// Zoom extents for the preview window & print catalog
+void PieceInfo::ZoomExtents()
+{
+ // TODO: Calculate this in the right way
+ bool out = false;
+ double modelMatrix[16], projMatrix[16];
+ double obj1x, obj1y, obj1z, obj2x, obj2y, obj2z;
+ double View[3] = { -5, -5, 3 };
+ int viewport[4];
+ float v[24] = {
+ m_fDimensions[0], m_fDimensions[1], m_fDimensions[5],
+ m_fDimensions[3], m_fDimensions[1], m_fDimensions[5],
+ m_fDimensions[0], m_fDimensions[1], m_fDimensions[2],
+ m_fDimensions[3], m_fDimensions[4], m_fDimensions[5],
+ m_fDimensions[3], m_fDimensions[4], m_fDimensions[2],
+ m_fDimensions[0], m_fDimensions[4], m_fDimensions[2],
+ m_fDimensions[0], m_fDimensions[4], m_fDimensions[5],
+ m_fDimensions[3], m_fDimensions[1], m_fDimensions[2] };
+
+ gluLookAt (View[0], View[1], View[2], (m_fDimensions[0] + m_fDimensions[3])/2, (m_fDimensions[1] + m_fDimensions[4])/2, (m_fDimensions[2] + m_fDimensions[5])/2, 0, 0, 1);
+ glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
+ glGetDoublev(GL_PROJECTION_MATRIX,projMatrix);
+ glGetIntegerv(GL_VIEWPORT,viewport);
+
+ gluUnProject((double)(viewport[2]/2),(double)(viewport[3]/2),0,
+ modelMatrix,projMatrix,viewport,&obj1x,&obj1y,&obj1z);
+ gluUnProject((double)(viewport[2]/2),(double)(viewport[3]/2),1,
+ modelMatrix,projMatrix,viewport,&obj2x,&obj2y,&obj2z);
+
+ double d = 2*sqrt((obj2x-obj1x)*(obj2x-obj1x)+(obj2y-obj1y)*(obj2y-obj1y)+(obj2z-obj1z)*(obj2z-obj1z));
+ while (!out) // Zoom in
+ {
+ View[0] += (obj2x-obj1x)/d;
+ View[1] += (obj2y-obj1y)/d;
+ View[2] += (obj2z-obj1z)/d;
+ glLoadIdentity();
+ gluLookAt (View[0], View[1], View[2], (m_fDimensions[0] + m_fDimensions[3])/2, (m_fDimensions[1] + m_fDimensions[4])/2, (m_fDimensions[2] + m_fDimensions[5])/2, 0, 0, 1);
+
+ for (int i = 0; i < 24; i+=3)
+ {
+ double winx, winy, winz;
+ if (gluProject (v[i], v[i+1], v[i+2], modelMatrix, projMatrix, viewport, &winx, &winy, &winz) == GL_TRUE)
+ if ((winx < viewport[0] + 1) || (winy < viewport[1] + 1) ||
+ (winx > viewport[2] - 1) || (winy > viewport[3] - 1))
+ out = true;
+ }
+
+ glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
+ glGetDoublev(GL_PROJECTION_MATRIX,projMatrix);
+ glGetIntegerv(GL_VIEWPORT,viewport);
+ }
+
+ while (out)
+ {
+ out = false;
+ View[0] -= (obj2x-obj1x)/d;
+ View[1] -= (obj2y-obj1y)/d;
+ View[2] -= (obj2z-obj1z)/d;
+ glLoadIdentity();
+ gluLookAt (View[0], View[1], View[2], (m_fDimensions[0] + m_fDimensions[3])/2, (m_fDimensions[1] + m_fDimensions[4])/2, (m_fDimensions[2] + m_fDimensions[5])/2, 0, 0, 1);
+
+ for (int i = 0; i < 24; i+=3)
+ {
+ double winx, winy, winz;
+ if (gluProject (v[i], v[i+1], v[i+2], modelMatrix, projMatrix, viewport, &winx, &winy, &winz) == GL_TRUE)
+ if ((winx < viewport[0] + 1) || (winy < viewport[1] + 1) ||
+ (winx > viewport[2] - 1) || (winy > viewport[3] - 1))
+ out = true;
+ }
+ glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
+ glGetDoublev(GL_PROJECTION_MATRIX,projMatrix);
+ glGetIntegerv(GL_VIEWPORT,viewport);
+ }
+}
+
+// Used by the print catalog and HTML instructions functions.
+void PieceInfo::RenderOnce(int nColor)
+{
+ AddRef();
+ RenderPiece(nColor);
+ DeRef();
+}
+
+// Called by the piece preview and from RenderOnce()
+void PieceInfo::RenderPiece(int nColor)
+{
+ unsigned short sh, curcolor;
+ DRAWGROUP* pGroup;
+
+ for (sh = 0; sh < m_nTextureCount; sh++)
+ {
+// if (!m_pTextures[sh].texture->IsLoaded())
+// m_pTextures[sh].texture->Load(false);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+ m_pTextures[sh].texture->MakeCurrent();
+
+ if (m_pTextures[sh].color == LC_COL_DEFAULT)
+ glColor3fv(FlatColorArray[nColor]);
+ if (nColor > 13 && nColor < 22)
+ {
+// glEnable (GL_POLYGON_STIPPLE);
+ glEnable (GL_BLEND);
+ glDepthMask (GL_FALSE);
+ }
+ else
+ {
+// glDisable (GL_POLYGON_STIPPLE);
+ glDepthMask (GL_TRUE);
+ glDisable (GL_BLEND);
+ }
+
+ glEnable(GL_TEXTURE_2D);
+ glBegin(GL_QUADS);
+ glTexCoord2fv(m_pTextures[sh].coords[0]);
+ glVertex3fv(m_pTextures[sh].vertex[0]);
+ glTexCoord2fv(m_pTextures[sh].coords[1]);
+ glVertex3fv(m_pTextures[sh].vertex[1]);
+ glTexCoord2fv(m_pTextures[sh].coords[2]);
+ glVertex3fv(m_pTextures[sh].vertex[2]);
+ glTexCoord2fv(m_pTextures[sh].coords[3]);
+ glVertex3fv(m_pTextures[sh].vertex[3]);
+ glEnd();
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer (3, GL_FLOAT, 0, m_fVertexArray);
+
+ sh = m_nGroupCount;
+ for (pGroup = m_pGroups; sh--; pGroup++)
+ {
+ if (m_nFlags & LC_PIECE_LONGDATA)
+ {
+ unsigned long* info, colors;
+
+ info = (unsigned long*)pGroup->drawinfo;
+ colors = *info;
+ info++;
+
+ while (colors--)
+ {
+ if (*info == LC_COL_DEFAULT)
+ curcolor = nColor;
+ else
+ curcolor = (unsigned short)*info;
+ info++;
+
+ glColor3fv(FlatColorArray[curcolor]);
+ if (curcolor > 13 && curcolor < 22)
+ {
+// glEnable (GL_POLYGON_STIPPLE);
+ glEnable (GL_BLEND);
+ glDepthMask (GL_FALSE);
+ }
+ else
+ {
+// glDisable (GL_POLYGON_STIPPLE);
+ glDepthMask (GL_TRUE);
+ glDisable (GL_BLEND);
+ }
+
+ if (*info)
+ glDrawElements(GL_QUADS, *info, GL_UNSIGNED_INT, info+1);
+ info += *info + 1;
+ if (*info)
+ glDrawElements(GL_TRIANGLES, *info, GL_UNSIGNED_INT, info+1);
+ info += *info + 1;
+ if (*info)
+ glDrawElements(GL_LINES, *info, GL_UNSIGNED_INT, info+1);
+ info += *info + 1;
+ }
+ }
+ else
+ {
+ unsigned short* info, colors;
+
+ info = (unsigned short*)pGroup->drawinfo;
+ colors = *info;
+ info++;
+
+ while (colors--)
+ {
+ if (*info == LC_COL_DEFAULT)
+ curcolor = nColor;
+ else
+ curcolor = *info;
+ info++;
+
+ glColor3fv(FlatColorArray[curcolor]);
+ if (curcolor > 13 && curcolor < 22)
+ {
+// glEnable (GL_POLYGON_STIPPLE);
+ glEnable (GL_BLEND);
+ glDepthMask (GL_FALSE);
+ }
+ else
+ {
+// glDisable (GL_POLYGON_STIPPLE);
+ glDepthMask (GL_TRUE);
+ glDisable (GL_BLEND);
+ }
+
+ if (*info)
+ glDrawElements(GL_QUADS, *info, GL_UNSIGNED_SHORT, info+1);
+ info += *info + 1;
+ if (*info)
+ glDrawElements(GL_TRIANGLES, *info, GL_UNSIGNED_SHORT, info+1);
+ info += *info + 1;
+ if (*info)
+ glDrawElements(GL_LINES, *info, GL_UNSIGNED_SHORT, info+1);
+ info += *info + 1;
+ }
+ }
+ }
+}
+
+void PieceInfo::WriteWavefront(FILE* file, unsigned char color, unsigned long* start)
+{
+ unsigned short group;
+ const char* colname;
+
+ for (group = 0; group < m_nGroupCount; group++)
+ {
+ if (m_nFlags & LC_PIECE_LONGDATA)
+ {
+ unsigned long* info = (unsigned long*)m_pGroups[group].drawinfo;
+ unsigned long count, colors = *info;
+ info++;
+
+ while (colors--)
+ {
+ if (*info == LC_COL_DEFAULT)
+ colname = altcolornames[color];
+ else
+ {
+ if (*info >= LC_MAXCOLORS)
+ {
+ info++;
+ info += *info + 1;
+ info += *info + 1;
+ info += *info + 1;
+ continue;
+ }
+ colname = altcolornames[*info];
+ }
+ info++;
+
+ // skip if color only have lines
+ if ((*info == 0) && (info[1] == 0))
+ {
+ info += 2;
+ info += *info + 1;
+ continue;
+ }
+
+ fprintf(file, "usemtl %s\n", colname);
+
+ for (count = *info, info++; count; count -= 4)
+ {
+ fprintf(file, "f %d %d %d %d\n",
+ *info+*start, info[1]+*start, info[2]+*start, info[3]+*start);
+ info += 4;
+ }
+
+ for (count = *info, info++; count; count -= 3)
+ {
+ fprintf(file, "f %d %d %d\n",
+ *info+*start, info[1]+*start, info[2]+*start);
+ info += 3;
+ }
+ info += *info + 1;
+ }
+ }
+ else
+ {
+ unsigned short* info = (unsigned short*)m_pGroups[group].drawinfo;
+ unsigned short count, colors = *info;
+ info++;
+
+ while (colors--)
+ {
+ if (*info == LC_COL_DEFAULT)
+ colname = altcolornames[color];
+ else
+ {
+ if (*info >= LC_MAXCOLORS)
+ {
+ info++;
+ info += *info + 1;
+ info += *info + 1;
+ info += *info + 1;
+ continue;
+ }
+ colname = altcolornames[*info];
+ }
+ info++;
+
+ // skip if color only have lines
+ if ((*info == 0) && (info[1] == 0))
+ {
+ info += 2;
+ info += *info + 1;
+ continue;
+ }
+
+ fprintf(file, "usemtl %s\n", colname);
+
+ for (count = *info, info++; count; count -= 4)
+ {
+ fprintf(file, "f %d %d %d %d\n",
+ *info+*start, info[1]+*start, info[2]+*start, info[3]+*start);
+ info += 4;
+ }
+
+ for (count = *info, info++; count; count -= 3)
+ {
+ fprintf(file, "f %d %d %d\n",
+ *info+*start, info[1]+*start, info[2]+*start);
+ info += 3;
+ }
+ info += *info + 1;
+ }
+
+ }
+ }
+
+ *start += m_nVertexCount;
+ fputs("\n", file);
+}
diff --git a/common/pieceinf.h b/common/pieceinf.h
new file mode 100644
index 0000000..f0e4db6
--- /dev/null
+++ b/common/pieceinf.h
@@ -0,0 +1,92 @@
+//
+// pieceinf.h
+////////////////////////////////////////////////////
+
+#ifndef _PIECEINF_H_
+#define _PIECEINF_H_
+
+#ifndef GLuint
+#include <GL/gl.h>
+#endif
+
+#define LC_PIECE_COUNT 0x01 // Count this piece in the totals ?
+#define LC_PIECE_LONGDATA 0x02 // unsigned long/short index
+#define LC_PIECE_CCW 0x04 // Use back-face culling
+#define LC_PIECE_SMALL 0x10 // scale = 10000
+#define LC_PIECE_MEDIUM 0x20 // scale = 1000 (otherwise = 100)
+
+class File;
+class Texture;
+
+typedef struct
+{
+ unsigned char type;
+ float center[3];
+ float normal[3];
+} CONNECTIONINFO;
+
+typedef struct
+{
+ unsigned short connections[6];
+ void* drawinfo;
+} DRAWGROUP;
+
+typedef struct TEXTURE
+{
+ Texture* texture;
+ unsigned char color;
+ float vertex[4][3];
+ float coords[4][2];
+} TEXTURE;
+
+unsigned char ConvertColor(int c);
+
+class PieceInfo
+{
+public:
+ PieceInfo();
+ ~PieceInfo();
+
+ // Operations
+ void ZoomExtents();
+ void RenderOnce(int nColor);
+ void RenderPiece(int nColor);
+ void WriteWavefront(FILE* file, unsigned char color, unsigned long* start);
+
+ // Implementation
+ void LoadIndex(File* file);
+ GLuint AddRef();
+ void DeRef();
+
+public:
+ // Attributes
+ char m_strName[9];
+ char m_strDescription[65];
+ float m_fDimensions[6];
+ unsigned long m_nOffset;
+ unsigned long m_nSize;
+ unsigned long m_nGroups;
+
+ // Nobody should change these
+ unsigned char m_nFlags;
+ unsigned long m_nVertexCount;
+ float* m_fVertexArray;
+ unsigned short m_nConnectionCount;
+ CONNECTIONINFO* m_pConnections;
+ unsigned short m_nGroupCount;
+ DRAWGROUP* m_pGroups;
+ unsigned char m_nTextureCount;
+ TEXTURE* m_pTextures;
+
+protected:
+ int m_nRef;
+ GLuint m_nBoxList;
+
+ void LoadInformation();
+ void FreeInformation();
+/*
+ CRModel* m_pRModel;
+*/
+};
+
+#endif // _PIECEINF_H_
diff --git a/common/project.cpp b/common/project.cpp
new file mode 100644
index 0000000..80a754a
--- /dev/null
+++ b/common/project.cpp
@@ -0,0 +1,7063 @@
+// Everything that is a part of a LeoCAD project goes here.
+//
+
+#ifdef _WINDOWS
+#include "stdafx.h"
+#else
+#include <GL/gl.h>
+#include <GL/glu.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <float.h>
+#include <math.h>
+#include "vector.h"
+#include "matrix.h"
+#include "pieceinf.h"
+#include "texture.h"
+#include "piece.h"
+#include "camera.h"
+#include "light.h"
+#include "group.h"
+#include "terrain.h"
+#include "project.h"
+#include "image.h"
+#include "system.h"
+
+typedef struct
+{
+ unsigned char n;
+ float dim [4][4];
+} VIEWPORT;
+
+static VIEWPORT viewports[14] = {
+ { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 1
+ { 2, 0, 0, 0.5f, 1, 0.5f, 0, 0.5f, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, // 2V
+ { 2, 0, 0, 1, 0.5f, 0, 0.5f, 1, 0.5f, 0, 0, 0, 0, 0, 0, 0, 0 }, // 2H
+ { 2, 0, 0, 1, 0.7f, 0, 0.7f, 1, 0.3f, 0, 0, 0, 0, 0, 0, 0, 0 }, // 2HT
+ { 2, 0, 0, 1, 0.3f, 0, 0.3f, 1, 0.7f, 0, 0, 0, 0, 0, 0, 0, 0 }, // 2HB
+ { 3, 0, 0, 0.5f, 0.5f, 0, 0.5f, 0.5f, 0.5f, 0.5f, 0, 0.5f, 1, 0, 0, 0, 0 }, // 3VL
+ { 3, 0, 0, 0.5f, 1, 0.5f, 0, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0, 0, 0, 0 }, // 3VR
+ { 3, 0, 0, 1, 0.5f, 0, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0, 0, 0, 0 }, // 3HB
+ { 3, 0, 0, 0.5f, 0.5f, 0.5f, 0, 0.5f, 0.5f, 0, 0.5f, 1, 0.5f, 0, 0, 0, 0 }, // 3HT
+ { 4, 0, 0, 0.3f, 0.3f, 0, 0.3f, 0.3f, 0.4f, 0, 0.7f, 0.3f, 0.3f, 0.3f, 0, 0.7f, 1 }, // 4VL
+ { 4, 0, 0, 0.7f, 1, 0.7f, 0, 0.3f, 0.3f, 0.7f, 0.3f, 0.3f, 0.4f, 0.7f, 0.7f, 0.3f, 0.3f }, // 4VR
+ { 4, 0, 0, 1, 0.7f, 0, 0.7f, 0.3f, 0.3f, 0.3f, 0.7f, 0.4f, 0.3f, 0.7f, 0.7f, 0.3f, 0.3f }, // 4HT
+ { 4, 0, 0, 0.3f, 0.3f, 0.3f, 0, 0.4f, 0.3f, 0.7f, 0, 0.3f, 0.3f, 0, 0.3f, 1, 0.7f }, // 4HB
+ { 4, 0, 0, 0.5f, 0.5f, 0.5f, 0, 0.5f, 0.5f, 0, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f }};// 4
+
+typedef struct
+{
+ unsigned char width;
+ float left, right, top, bottom;
+} TXFVERT;
+
+static TXFVERT glyphs[93];
+
+/////////////////////////////////////////////////////////////////////////////
+// Project construction/destruction
+
+Project::Project()
+{
+ m_bModified = false;
+ m_bTrackCancel = false;
+ m_nTracking = LC_TRACK_NONE;
+ m_pPieceIdx = NULL;
+ m_nPieceCount = 0;
+ m_pTextures = NULL;
+ m_nTextureCount = 0;
+ m_pPieces = NULL;
+ m_pCameras = NULL;
+ m_pLights = NULL;
+ m_pGroups = NULL;
+ m_pMovedReference = NULL;
+ m_nMovedCount = 0;
+ m_pUndoList = NULL;
+ m_pRedoList = NULL;
+ m_nGridList = 0;
+ m_nCurClipboard = 0;
+ m_pTerrain = new Terrain();
+ m_pBackground = new Texture();
+ m_nAutosave = SystemGetProfileInt("Settings", "Autosave", 10);
+ m_nMouse = SystemGetProfileInt("Default", "Mouse", 11);
+ strcpy(m_strModelsPath, SystemGetProfileString("Default", "Projects", ""));
+
+ int i;
+ for (i = 0; i < LC_CONNECTIONS; i++)
+ {
+ m_pConnections[i].entries = NULL;
+ m_pConnections[i].numentries = 0;
+ }
+
+ for (i = 0; i < 10; i++)
+ m_pClipboard[i] = NULL;
+
+ char entry[8];
+ for (i = 0; i < 4; i++)
+ {
+ sprintf(entry, "File%d", i+1);
+ strcpy(m_strRecentFiles[i], SystemGetProfileString("RecentFiles", entry, ""));
+ }
+
+ // Create font table
+ float inv = 1.0f/128;
+ char *charlines[16] = {
+ "abcdefghijklmn", "opqrstuvwxyz0", "123456789ABC", "DEFGHIJKLMN",
+ "OPQRSTUVWX", "YZ,.!;:<>/?{}@$%", "&*()-+=_[] #" };
+ unsigned char lefts[7][17] = {
+ { 1, 11, 21, 30, 40, 50, 56, 66, 76, 80, 84, 93, 97, 111, 121 },
+ { 1, 11, 21, 31, 38, 47, 53, 63, 72, 83, 94, 103, 111, 120 },
+ { 1, 10, 19, 28, 37, 46, 55, 64, 73, 82, 94, 106, 118, },
+ { 1, 13, 24, 34, 47, 59, 64, 73, 84, 94, 108, 120 },
+ { 1, 14, 25, 38, 50, 61, 71, 83, 94, 109, 120 },
+ { 1, 12, 22, 26, 30, 35, 39, 43, 52, 61, 65, 75, 81, 87, 103, 112, 125 },
+ { 3, 14, 23, 28, 33, 38, 47, 56, 65, 70, 75, 79, 88 } };
+ // tops = 1 20 39 58 77 96 112 (+16)
+ memset(glyphs, 0, sizeof(glyphs));
+
+ // ASCII 32-125
+ for (i = 32; i < 126; i++)
+ for (int x = 0; x < 7; x++)
+ for (int y = 0; charlines[x][y]; y++)
+ if (charlines[x][y] == i)
+ {
+ glyphs[i-32].width = lefts[x][y+1] - lefts[x][y];
+ glyphs[i-32].left = (float)lefts[x][y]*inv;
+ glyphs[i-32].right = (float)(lefts[x][y+1])*inv;
+ if (x != 6)
+ glyphs[i-32].top = (float)(1 + 19*x);
+ else
+ glyphs[i-32].top = 112;
+ glyphs[i-32].bottom = glyphs[i-32].top + 16;
+ glyphs[i-32].top *= inv;
+ glyphs[i-32].bottom *= inv;
+ }
+}
+
+Project::~Project()
+{
+ DeleteContents(false);
+ SystemFinish();
+
+ if (m_pPieceIdx != NULL)
+ {
+ PieceInfo* pInfo;
+ for (pInfo = m_pPieceIdx; m_nPieceCount--; pInfo++)
+ pInfo->~PieceInfo();
+ delete [] m_pPieceIdx;
+ }
+
+ if (m_pTextures != NULL)
+ {
+ Texture* pTexture;
+ for (pTexture = m_pTextures; m_nTextureCount--; pTexture++)
+ pTexture->~Texture();
+ delete [] m_pTextures;
+ }
+
+ if (m_pMovedReference != NULL)
+ free(m_pMovedReference);
+
+ int i;
+ char entry[8];
+ for (i = 0; i < 4; i++)
+ {
+ sprintf(entry, "File%d", i+1);
+// if (strlen(m_strRecentFiles[i]) > 0)
+ SystemSetProfileString("RecentFiles", entry, m_strRecentFiles[i]);
+ }
+
+ for (i = 0; i < 10; i++)
+ if (m_pClipboard[i] != NULL)
+ delete m_pClipboard[i];
+
+ delete m_pTerrain;
+ delete m_pBackground;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Project attributes, general services
+
+// The main window should be created before calling this
+bool Project::Initialize(int argc, char *argv[], char* libpath)
+{
+ m_LibraryPath = libpath;
+ m_strPathName[0] = 0;
+
+ LC_IMAGE_OPTS imopts;
+ char picture[LC_MAXPATH];
+ picture[0] = 0;
+
+ unsigned long image = SystemGetProfileInt ("Default", "Image Options", 1|LC_IMAGE_TRANSPARENT);
+ int width = SystemGetProfileInt("Default", "Image Width", 640);
+ int height = SystemGetProfileInt("Default", "Image Height", 480);
+// int width = SystemGetProfileInt("Default", "Image Width", GetSystemMetrics(SM_CXSCREEN));
+// int height = SystemGetProfileInt("Default", "Image Height", GetSystemMetrics(SM_CYSCREEN));
+ imopts.quality = SystemGetProfileInt("Default", "JPEG Quality", 70);
+ imopts.interlaced = (image & LC_IMAGE_PROGRESSIVE) != 0;
+ imopts.transparent = (image & LC_IMAGE_TRANSPARENT) != 0;
+ imopts.truecolor = (image & LC_IMAGE_HIGHCOLOR) != 0;
+ imopts.format = (unsigned char)(image & ~(LC_IMAGE_MASK));
+
+ for (int i = 1; i < argc; i++)
+ {
+ char* param = argv[i];
+
+ if (argv[i][0] == '-')
+ {
+ ++param;
+
+ if ((strcmp (param, "l") == 0) && ((i+1) < argc))
+ {
+ i++;
+ m_LibraryPath = argv[i];
+ }
+
+ if (strcmp (param, "i") == 0)
+ {
+ if (((i+1) != argc) && (argv[i+1][0] != '-'))
+ {
+ i++;
+ strcpy(picture, argv[i]);
+ }
+ else
+ picture[0] = 1;
+ }
+
+ if ((strcmp (param, "w") == 0) && ((i+1) < argc))
+ {
+ int w;
+ i++;
+ if (sscanf(argv[i], "%d", &w) == 1)
+ width = w;
+ }
+
+ if ((strcmp (param, "h") == 0) && ((i+1) < argc))
+ {
+ int h;
+ i++;
+ if (sscanf(argv[i], "%d", &h) == 1)
+ height = h;
+ }
+ }
+ else
+ {
+ strcpy(m_strPathName, param);
+/*
+ if (m_strFileName.IsEmpty())
+ m_strFileName = pszParam;
+ else if (m_nShellCommand == FilePrintTo && m_strPrinterName.IsEmpty())
+ m_strPrinterName = pszParam;
+ else if (m_nShellCommand == FilePrintTo && m_strDriverName.IsEmpty())
+ m_strDriverName = pszParam;
+ else if (m_nShellCommand == FilePrintTo && m_strPortName.IsEmpty())
+ m_strPortName = pszParam;
+*/
+ }
+ }
+
+ if (LoadPieceLibrary() == false)
+ {
+ SystemDoMessageBox("Cannot load piece library.", LC_MB_OK|LC_MB_ICONERROR);
+ return false;
+ }
+
+ SystemInit();
+
+ if (strlen(m_strPathName) && OnOpenDocument(m_strPathName))
+ {
+ SetPathName(m_strPathName, true);
+
+ if (picture[0] != 0)
+ {
+ bool need_ext = false;
+
+ if (picture[0] == 1)
+ {
+ strcpy(picture, m_strPathName);
+ char *p = strrchr(picture, '.');
+ if (p != NULL)
+ *p = 0;
+ need_ext = true;
+ }
+ else
+ {
+ char ext[5];
+ char *p = strrchr(picture, '.');
+ if (p != NULL)
+ strcpy(ext, p+1);
+ strlwr(ext);
+
+ if ((strcmp(ext, "bmp") != 0) && (strcmp(ext, "gif") != 0) &&
+ (strcmp(ext, "jpg") != 0) && (strcmp(ext, "jpeg") != 0))
+ need_ext = true;
+ }
+
+ if (need_ext)
+ switch (imopts.format)
+ {
+ case 0: strcat(picture, ".bmp"); break;
+ case 1: strcat(picture, ".gif"); break;
+ case 2: strcat(picture, ".jpg"); break;
+ }
+
+ imopts.background[0] = (unsigned char)(m_fBackground[0]*255);
+ imopts.background[1] = (unsigned char)(m_fBackground[1]*255);
+ imopts.background[2] = (unsigned char)(m_fBackground[2]*255);
+
+ LC_IMAGE* image;
+ CreateImages(&image, width, height, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation ? m_nCurFrame : m_nCurStep, false);
+ SaveImage(picture, image, &imopts);
+ free(image);
+
+ return false;
+ }
+ }
+ else
+ OnNewDocument();
+
+ return true;
+}
+
+// Load the piece library
+bool Project::LoadPieceLibrary()
+{
+ File idx(false);
+ char filename[LC_MAXPATH];
+ unsigned char version;
+ unsigned short count, movedcount;
+ unsigned long binsize;
+ PieceInfo* pElements;
+ Texture* pTexture;
+ int i;
+
+ // Read the piece library index.
+ strcpy(filename, m_LibraryPath);
+ strcat(filename, "pieces.idx");
+
+ if (!idx.Open(filename, "rb"))
+ return false;
+
+ idx.Seek(-(long)(2*sizeof(count)+sizeof(binsize)), SEEK_END);
+ idx.Read(&movedcount, sizeof(movedcount));
+ idx.Read(&binsize, sizeof(binsize));
+ idx.Read(&count, sizeof(count));
+ idx.Seek(32, SEEK_SET);
+ idx.Read(&version, sizeof(version));
+
+ if ((version != 3) || (count == 0))
+ {
+ idx.Close();
+ return false;
+ }
+ idx.Seek(34, SEEK_SET); // skip update byte
+
+ // TODO: check .bin file size.
+
+ if (m_pPieceIdx != NULL)
+ {
+ // call the destructors
+ for (pElements = m_pPieceIdx; m_nPieceCount--; pElements++)
+ pElements->~PieceInfo();
+ delete [] m_pPieceIdx;
+ }
+
+ m_pPieceIdx = new PieceInfo[count];
+ m_nPieceCount = count;
+ memset(m_pPieceIdx, 0, count * sizeof(PieceInfo));
+
+ for (pElements = m_pPieceIdx; count--; pElements++)
+ pElements->LoadIndex(&idx);
+
+ // Load moved files reference.
+ if (m_pMovedReference != NULL)
+ free(m_pMovedReference);
+ m_pMovedReference = (char*)malloc(18*movedcount);
+ memset(m_pMovedReference, 0, 18*movedcount);
+ m_nMovedCount = movedcount;
+
+ for (i = 0; i < movedcount; i++)
+ {
+ idx.Read(&m_pMovedReference[i*18], 8);
+ idx.Read(&m_pMovedReference[i*18+9], 8);
+ }
+
+ idx.Close();
+
+ // TODO: Load group configuration here
+
+ // Read the texture index.
+ strcpy(filename, m_LibraryPath);
+ strcat(filename, "textures.idx");
+
+ if (m_pTextures != NULL)
+ {
+ // call the destructors
+ for (pTexture = m_pTextures; m_nTextureCount--; pTexture++)
+ pTexture->~Texture();
+ delete [] m_pTextures;
+
+ m_pTextures = NULL;
+ m_nTextureCount = 0;
+ }
+
+ if (!idx.Open(filename, "rb"))
+ return false;
+
+ idx.Seek(-(long)(sizeof(count)+sizeof(binsize)), SEEK_END);
+ idx.Read(&binsize, sizeof(binsize));
+ idx.Read(&count, sizeof(count));
+ idx.Seek(32, SEEK_SET);
+ idx.Read(&version, sizeof(version));
+
+ if ((version != 1) || (count == 0))
+ {
+ idx.Close();
+ return false;
+ }
+ idx.Seek(34, SEEK_SET); // skip update byte
+
+ // TODO: check .bin file size.
+
+ m_pTextures = new Texture[count];
+ m_nTextureCount = count;
+ memset(m_pTextures, 0, count * sizeof(Texture));
+
+ for (pTexture = m_pTextures; count--; pTexture++)
+ pTexture->LoadIndex(&idx);
+
+ idx.Close();
+
+ return true;
+}
+
+void Project::SetTitle(char* lpszTitle)
+{
+ strcpy(m_strTitle, lpszTitle);
+
+ char title[LC_MAXPATH], *ptr, ext[4];
+ strcpy(title, "LeoCAD - ");
+ strcat(title, m_strTitle);
+
+ ptr = strrchr(title, '.');
+ if (ptr != NULL)
+ {
+ strncpy(ext, ptr+1, 3);
+ ext[3] = 0;
+ strlwr(ext);
+
+ if (strcmp(ext, "lcd") == 0)
+ *ptr = 0;
+ if (strcmp(ext, "dat") == 0)
+ *ptr = 0;
+ }
+
+ SystemSetWindowCaption(title);
+}
+
+void Project::DeleteContents(bool bUndo)
+{
+ Piece* pPiece;
+ Camera* pCamera;
+ Light* pLight;
+ Group* pGroup;
+
+ memset(m_strAuthor, 0, sizeof(m_strAuthor));
+ memset(m_strDescription, 0, sizeof(m_strDescription));
+ memset(m_strComments, 0, sizeof(m_strComments));
+
+ if (!bUndo)
+ {
+ UNDOINFO* pUndo;
+
+ while (m_pUndoList)
+ {
+ pUndo = m_pUndoList;
+ m_pUndoList = m_pUndoList->pNext;
+ delete pUndo;
+ }
+
+ while (m_pRedoList)
+ {
+ pUndo = m_pRedoList;
+ m_pRedoList = m_pRedoList->pNext;
+ delete pUndo;
+ }
+
+ m_pRedoList = NULL;
+ m_pUndoList = NULL;
+
+ m_pBackground->Unload();
+ m_pTerrain->LoadDefaults((m_nDetail & LC_DET_LINEAR) != 0);
+ }
+
+ while (m_pPieces)
+ {
+ pPiece = m_pPieces;
+ m_pPieces = m_pPieces->m_pNext;
+ delete pPiece;
+ }
+
+ for (int i = 0; i < LC_CONNECTIONS; i++)
+ {
+ for (int j = 0; j < m_pConnections[i].numentries; j++)
+ delete (m_pConnections[i].entries[j].cons);
+
+ delete m_pConnections[i].entries;
+ m_pConnections[i].entries = NULL;
+ m_pConnections[i].numentries = 0;
+ }
+
+ while (m_pCameras)
+ {
+ pCamera = m_pCameras;
+ m_pCameras = m_pCameras->m_pNext;
+ delete pCamera;
+ }
+
+ while (m_pLights)
+ {
+ pLight = m_pLights;
+ m_pLights = m_pLights->m_pNext;
+ delete pLight;
+ }
+
+ while (m_pGroups)
+ {
+ pGroup = m_pGroups;
+ m_pGroups = m_pGroups->m_pNext;
+ delete pGroup;
+ }
+
+
+/*
+ if (!m_strTempFile.IsEmpty())
+ {
+ DeleteFile (m_strTempFile);
+ m_strTempFile.Empty();
+ }
+*/
+}
+
+// Only call after DeleteContents()
+void Project::LoadDefaults(bool cameras)
+{
+ int i;
+ unsigned long rgb;
+
+ // Default values
+ m_nActiveViewport = 0;
+ SystemUpdateViewport(0, m_nViewportMode);
+ m_nViewportMode = 0;
+ SetAction(0);
+ m_nCurColor = 0;
+ SystemUpdateColorList(m_nCurColor);
+ m_nCurGroup = 1;
+ SystemSetGroup(m_nCurGroup);
+ m_bAnimation = false;
+ m_bAddKeys = false;
+ SystemUpdateAnimation(m_bAnimation, m_bAddKeys);
+ m_bUndoOriginal = true;
+ SystemUpdateUndoRedo(NULL, NULL);
+ m_nDetail = SystemGetProfileInt("Default", "Detail", LC_DET_BRICKEDGES);
+ SystemUpdateRenderingMode((m_nDetail & LC_DET_BACKGROUND) != 0, (m_nDetail & LC_DET_FAST) != 0);
+ m_nAngleSnap = (unsigned short)SystemGetProfileInt("Default", "Angle", 30);
+ m_nSnap = SystemGetProfileInt("Default", "Snap", LC_DRAW_SNAP_A | LC_DRAW_SNAP_X | LC_DRAW_SNAP_Y | LC_DRAW_SNAP_Z | LC_DRAW_MOVE | LC_DRAW_PREVIEW);
+ SystemUpdateSnap(m_nSnap);
+ m_nMoveSnap = 0;
+ SystemUpdateMoveSnap(m_nMoveSnap);
+ m_fLineWidth = (float)SystemGetProfileInt("Default", "Line", 100)/100;
+ m_fFogDensity = (float)SystemGetProfileInt("Default", "Density", 10)/100;
+ rgb = SystemGetProfileInt("Default", "Fog", 0xFFFFFF);
+ m_fFogColor[0] = (float)((unsigned char) (rgb))/255;
+ m_fFogColor[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
+ m_fFogColor[2] = (float)((unsigned char) ((rgb) >> 16))/255;
+ m_fFogColor[3] = 1.0f;
+ m_nGridSize = (unsigned short)SystemGetProfileInt("Default", "Grid", 20);
+ rgb = SystemGetProfileInt("Default", "Ambient", 0x4B4B4B);
+ m_fAmbient[0] = (float)((unsigned char) (rgb))/255;
+ m_fAmbient[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
+ m_fAmbient[2] = (float)((unsigned char) ((rgb) >> 16))/255;
+ m_fAmbient[3] = 1.0f;
+ rgb = SystemGetProfileInt("Default", "Background", 0xFFFFFF);
+ m_fBackground[0] = (float)((unsigned char) (rgb))/255;
+ m_fBackground[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
+ m_fBackground[2] = (float)((unsigned char) ((rgb) >> 16))/255;
+ m_fBackground[3] = 1.0f;
+ rgb = SystemGetProfileInt("Default", "Gradient1", 0xBF0000);
+ m_fGradient1[0] = (float)((unsigned char) (rgb))/255;
+ m_fGradient1[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
+ m_fGradient1[2] = (float)((unsigned char) ((rgb) >> 16))/255;
+ rgb = SystemGetProfileInt("Default", "Gradient2", 0xFFFFFF);
+ m_fGradient2[0] = (float)((unsigned char) (rgb))/255;
+ m_fGradient2[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
+ m_fGradient2[2] = (float)((unsigned char) ((rgb) >> 16))/255;
+ m_nFPS = SystemGetProfileInt("Default", "FPS", 24);
+ m_nCurStep = 1;
+ m_nCurFrame = 1;
+ m_nTotalFrames = 100;
+ SystemUpdateTime(false, 1, 255);
+ m_nScene = SystemGetProfileInt("Default", "Scene", 0);
+ m_nSaveTimer = 0;
+ strcpy(m_strHeader, SystemGetProfileString("Default", "Header", ""));
+ strcpy(m_strFooter, SystemGetProfileString("Default", "Footer", "Page &P"));
+ strcpy(m_strBackground, SystemGetProfileString("Default", "BMP", ""));
+ m_pTerrain->LoadDefaults((m_nDetail & LC_DET_LINEAR) != 0);
+ RenderInitialize();
+
+ if (cameras)
+ {
+ Camera* pCam;
+ for (pCam = NULL, i = 0; i < 7; i++)
+ {
+ pCam = new Camera(i, pCam);
+ if (m_pCameras == NULL)
+ m_pCameras = pCam;
+
+ switch (i)
+ {
+ case LC_CAMERA_MAIN: m_pViewCameras[0] = pCam; break;
+ case LC_CAMERA_FRONT: m_pViewCameras[1] = pCam; break;
+ case LC_CAMERA_TOP: m_pViewCameras[2] = pCam; break;
+ case LC_CAMERA_RIGHT: m_pViewCameras[3] = pCam; break;
+ }
+ }
+ SystemUpdateCameraMenu(m_pCameras);
+ SystemUpdateCurrentCamera(NULL, m_pViewCameras[0], m_pCameras);
+ }
+ SystemPieceComboAdd(NULL);
+ UpdateSelection();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Standard file menu commands
+
+// Read a .lcd file
+bool Project::FileLoad(File* file, bool bUndo, bool bMerge)
+{
+ int i, count;
+ char id[32];
+ unsigned long rgb;
+ float fv = 0.4f;
+ unsigned char ch, action;
+ unsigned short sh;
+
+ file->Seek(0, SEEK_SET);
+ file->Read(id, 32);
+ sscanf(&id[7], "%f", &fv);
+ if (fv > 0.4f)
+ file->Read(&fv, sizeof(fv));
+
+ file->Read(&rgb, sizeof(rgb));
+ if (!bMerge)
+ {
+ m_fBackground[0] = (float)((unsigned char) (rgb))/255;
+ m_fBackground[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
+ m_fBackground[2] = (float)((unsigned char) ((rgb) >> 16))/255;
+ }
+
+ if (fv < 0.6f) // old view
+ {
+ Camera* pCam;
+ for (pCam = NULL, i = 0; i < 7; i++)
+ {
+ pCam = new Camera(i, pCam);
+ if (m_pCameras == NULL)
+ m_pCameras = pCam;
+
+ switch (i)
+ {
+ case LC_CAMERA_MAIN: m_pViewCameras[0] = pCam; break;
+ case LC_CAMERA_FRONT: m_pViewCameras[1] = pCam; break;
+ case LC_CAMERA_TOP: m_pViewCameras[2] = pCam; break;
+ case LC_CAMERA_RIGHT: m_pViewCameras[3] = pCam; break;
+ }
+ }
+
+ double eye[3], target[3];
+ file->Read(&eye, sizeof(eye));
+ file->Read(&target, sizeof(target));
+ float tmp[3] = { (float)eye[0], (float)eye[1], (float)eye[2] };
+ pCam->ChangeKey(1, false, false, tmp, CK_EYE);
+ pCam->ChangeKey(1, true, false, tmp, CK_EYE);
+ tmp[0] = (float)target[0]; tmp[1] = (float)target[1]; tmp[2] = (float)target[2];
+ pCam->ChangeKey(1, false, false, tmp, CK_TARGET);
+ pCam->ChangeKey(1, true, false, tmp, CK_TARGET);
+
+ // Create up vector
+ Vector upvec(0,0,1), frontvec((float)(eye[0]-target[0]), (float)(eye[1]-target[1]), (float)(eye[2]-target[2])), sidevec;
+ frontvec.Normalize();
+ if (frontvec == upvec)
+ sidevec.FromFloat(1,0,0);
+ else
+ sidevec.Cross(frontvec, upvec);
+ upvec.Cross(sidevec, frontvec);
+ upvec.Normalize();
+ upvec.ToFloat(tmp);
+ pCam->ChangeKey(1, false, false, tmp, CK_UP);
+ pCam->ChangeKey(1, true, false, tmp, CK_UP);
+ }
+
+ if (bMerge)
+ file->Seek(32, SEEK_CUR);
+ else
+ {
+ file->Read(&i, 4); m_nAngleSnap = i;
+ file->Read(&m_nSnap, 4);
+ file->Read(&m_fLineWidth, 4);
+ file->Read(&m_nDetail, 4);
+ file->Read(&i, 4); m_nCurGroup = i;
+ file->Read(&i, 4); m_nCurColor = i;
+ file->Read(&i, 4); action = i;
+ file->Read(&i, 4); m_nCurStep = i;
+ }
+
+ if (fv > 0.8f)
+ file->Read(&m_nScene, sizeof(m_nScene));
+
+ file->Read(&count, sizeof(count));
+ while (count--)
+ {
+ if (fv > 0.4f)
+ {
+ char name[9];
+ Piece* pPiece = new Piece(NULL);
+ pPiece->FileLoad(file, name);
+ PieceInfo* pInfo = FindPieceInfo(name);
+ if (pInfo)
+ {
+ pPiece->SetPieceInfo(pInfo);
+
+ if (bMerge)
+ for (Piece* p = m_pPieces; p; p = p->m_pNext)
+ if (strcmp(p->GetName(), pPiece->GetName()) == 0)
+ {
+ pPiece->CreateName(m_pPieces);
+ break;
+ }
+
+ if (strlen(pPiece->GetName()) == 0)
+ pPiece->CreateName(m_pPieces);
+
+ AddPiece(pPiece);
+ if (!bUndo)
+ SystemPieceComboAdd(pInfo->m_strDescription);
+ }
+ else
+ delete pPiece;
+ }
+ else
+ {
+ char name[9];
+ float pos[3], rot[3], param[4];
+ unsigned char color, step, group;
+
+ file->Read(pos, sizeof(pos));
+ file->Read(rot, sizeof(rot));
+ file->Read(&color, sizeof(color));
+ file->Read(name, sizeof(name));
+ file->Read(&step, sizeof(step));
+ file->Read(&group, sizeof(group));
+
+ const unsigned char conv[20] = { 0,2,4,9,7,6,22,8,10,11,14,16,18,9,21,20,22,8,10,11 };
+ color = conv[color];
+
+ PieceInfo* pInfo = FindPieceInfo(name);
+ if (pInfo != NULL)
+ {
+ Piece* pPiece = new Piece(pInfo);
+ Matrix mat;
+
+ pPiece->Initialize(pos[0], pos[1], pos[2], step, 1, color);
+ pPiece->CreateName(m_pPieces);
+ AddPiece(pPiece);
+ mat.CreateOld(0,0,0, rot[0],rot[1],rot[2]);
+ mat.ToAxisAngle(param);
+ pPiece->ChangeKey(1, false, false, param, PK_ROTATION);
+ pPiece->ChangeKey(1, true, false, param, PK_ROTATION);
+// pPiece->SetGroup((Group*)group);
+ SystemPieceComboAdd(pInfo->m_strDescription);
+ }
+ }
+ }
+
+ if (!bMerge)
+ {
+ if (fv >= 0.4f)
+ {
+ file->Read(&ch, 1);
+ if (ch == 0xFF) file->Read(&sh, 2); else sh = ch;
+ if (sh > 100)
+ file->Seek(sh, SEEK_CUR);
+ else
+ file->Read(m_strAuthor, sh);
+
+ file->Read(&ch, 1);
+ if (ch == 0xFF) file->Read(&sh, 2); else sh = ch;
+ if (sh > 100)
+ file->Seek(sh, SEEK_CUR);
+ else
+ file->Read(m_strDescription, sh);
+
+ file->Read(&ch, 1);
+ if (ch == 0xFF && fv < 1.3f) file->Read(&sh, 2); else sh = ch;
+ if (sh > 255)
+ file->Seek(sh, SEEK_CUR);
+ else
+ file->Read(m_strComments, sh);
+ }
+ }
+ else
+ {
+ if (fv >= 0.4f)
+ {
+ file->Read(&ch, 1);
+ if (ch == 0xFF) file->Read(&sh, 2); else sh = ch;
+ file->Seek(sh, SEEK_CUR);
+
+ file->Read(&ch, 1);
+ if (ch == 0xFF) file->Read(&sh, 2); else sh = ch;
+ file->Seek(sh, SEEK_CUR);
+
+ file->Read(&ch, 1);
+ if (ch == 0xFF && fv < 1.3f) file->Read(&sh, 2); else sh = ch;
+ file->Seek(sh, SEEK_CUR);
+ }
+ }
+
+ if (fv >= 0.5f)
+ {
+ file->Read(&count, sizeof(count));
+
+ Group* pGroup;
+ Group* pLastGroup = NULL;
+ for (pGroup = m_pGroups; pGroup; pGroup = pGroup->m_pNext)
+ pLastGroup = pGroup;
+
+ pGroup = pLastGroup;
+ for (i = 0; i < count; i++)
+ {
+ if (pGroup)
+ {
+ pGroup->m_pNext = new Group();
+ pGroup = pGroup->m_pNext;
+ }
+ else
+ m_pGroups = pGroup = new Group();
+ }
+ pLastGroup = pLastGroup ? pLastGroup->m_pNext : m_pGroups;
+
+ for (pGroup = pLastGroup; pGroup; pGroup = pGroup->m_pNext)
+ {
+ if (fv < 1.0f)
+ {
+ file->Read(pGroup->m_strName, 65);
+ file->Read(&ch, 1);
+ pGroup->m_fCenter[0] = 0;
+ pGroup->m_fCenter[1] = 0;
+ pGroup->m_fCenter[2] = 0;
+ pGroup->m_pGroup = (Group*)-1;
+ }
+ else
+ pGroup->FileLoad(file);
+ }
+
+ for (pGroup = pLastGroup; pGroup; pGroup = pGroup->m_pNext)
+ {
+ i = (int)pGroup->m_pGroup;
+ pGroup->m_pGroup = NULL;
+
+ if (i > 0xFFFF || i == -1)
+ continue;
+
+ for (Group* g2 = pLastGroup; g2; g2 = g2->m_pNext)
+ {
+ if (i == 0)
+ {
+ pGroup->m_pGroup = g2;
+ break;
+ }
+
+ i--;
+ }
+ }
+
+
+ Piece* pPiece;
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ i = (int)pPiece->GetGroup();
+ pPiece->SetGroup(NULL);
+
+ if (i > 0xFFFF || i == -1)
+ continue;
+
+ for (pGroup = pLastGroup; pGroup; pGroup = pGroup->m_pNext)
+ {
+ if (i == 0)
+ {
+ pPiece->SetGroup(pGroup);
+ break;
+ }
+
+ i--;
+ }
+ }
+
+ RemoveEmptyGroups();
+ }
+
+ if (!bMerge)
+ {
+ if (fv >= 0.6f)
+ {
+ if (fv < 1.0f)
+ {
+ file->Read(&i, sizeof(i));
+ m_nViewportMode = i;
+ }
+ else
+ {
+ file->Read(&m_nViewportMode, 1);
+ file->Read(&m_nActiveViewport, 1);
+ }
+
+ file->Read(&count, 4);
+ Camera* pCam = NULL;
+ for (i = 0; i < count; i++)
+ {
+ pCam = new Camera(i, pCam);
+ if (m_pCameras == NULL)
+ m_pCameras = pCam;
+
+ if (i < 4 && fv == 0.6f)
+ m_pViewCameras[i] = pCam;
+ }
+
+ if (count < 7)
+ {
+ pCam = new Camera(0, NULL);
+ for (i = 0; i < count; i++)
+ pCam->FileLoad(file);
+ delete pCam;
+ }
+ else
+ for (pCam = m_pCameras; pCam; pCam = pCam->m_pNext)
+ pCam->FileLoad(file);
+ }
+
+ if (fv >= 0.7f)
+ {
+ for (count = 0; count < 4; count++)
+ {
+ file->Read(&i, 4);
+
+ Camera* pCam = m_pCameras;
+ while (i--)
+ pCam = pCam->m_pNext;
+ m_pViewCameras[count] = pCam;
+ }
+
+ file->Read(&rgb, sizeof(rgb));
+ m_fFogColor[0] = (float)((unsigned char) (rgb))/255;
+ m_fFogColor[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
+ m_fFogColor[2] = (float)((unsigned char) ((rgb) >> 16))/255;
+
+ if (fv < 1.0f)
+ {
+ file->Read(&rgb, sizeof(rgb));
+ m_fFogDensity = (float)rgb/100;
+ }
+ else
+ file->Read(&m_fFogDensity, sizeof(m_fFogDensity));
+
+ if (fv < 1.3f)
+ {
+ file->Read(&ch, 1);
+ if (ch == 0xFF)
+ file->Read(&sh, 2);
+ sh = ch;
+ }
+ else
+ file->Read(&sh, 2);
+
+ if (sh < LC_MAXPATH)
+ file->Read(m_strBackground, sh);
+ else
+ file->Seek(sh, SEEK_CUR);
+ }
+
+ if (fv >= 0.8f)
+ {
+ file->Read(&ch, 1);
+ file->Read(m_strHeader, ch);
+ file->Read(&ch, 1);
+ file->Read(m_strFooter, ch);
+ }
+
+ if (fv > 0.9f)
+ {
+ file->Read(&rgb, sizeof(rgb));
+ m_fAmbient[0] = (float)((unsigned char) (rgb))/255;
+ m_fAmbient[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
+ m_fAmbient[2] = (float)((unsigned char) ((rgb) >> 16))/255;
+
+ if (fv < 1.3f)
+ {
+ file->Read(&i, 4); m_bAnimation = (i != 0);
+ file->Read(&i, 4); m_bAddKeys = (i != 0);
+ file->Read(&m_nFPS, 1);
+ file->Read(&i, 4); m_nCurFrame = i;
+ file->Read(&m_nTotalFrames, 2);
+ file->Read(&i, 4); m_nGridSize = i;
+ file->Read(&i, 4); m_nMoveSnap = i;
+ }
+ else
+ {
+ file->Read(&ch, 1); m_bAnimation = (ch != 0);
+ file->Read(&ch, 1); m_bAddKeys = (ch != 0);
+ file->Read(&m_nFPS, 1);
+ file->Read(&m_nCurFrame, 2);
+ file->Read(&m_nTotalFrames, 2);
+ file->Read(&m_nGridSize, 2);
+ file->Read(&m_nMoveSnap, 2);
+ }
+ }
+
+ if (fv > 1.0f)
+ {
+ file->Read(&rgb, sizeof(rgb));
+ m_fGradient1[0] = (float)((unsigned char) (rgb))/255;
+ m_fGradient1[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
+ m_fGradient1[2] = (float)((unsigned char) ((rgb) >> 16))/255;
+ file->Read(&rgb, sizeof(rgb));
+ m_fGradient2[0] = (float)((unsigned char) (rgb))/255;
+ m_fGradient2[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
+ m_fGradient2[2] = (float)((unsigned char) ((rgb) >> 16))/255;
+
+ if (fv > 1.1f)
+ m_pTerrain->FileLoad(file);
+ else
+ {
+ file->Seek(4, SEEK_CUR);
+ file->Read(&ch, 1);
+ file->Seek(ch, SEEK_CUR);
+ }
+ }
+ }
+
+ RenderInitialize();
+ CalculateStep();
+ if (!bUndo)
+ SelectAndFocusNone(false);
+ if (!bMerge)
+ SystemUpdateFocus(NULL, LC_PIECE|LC_UPDATE_TYPE|LC_UPDATE_OBJECT);
+ SetAction(action);
+ SystemUpdateViewport(m_nViewportMode, 0);
+ SystemUpdateColorList(m_nCurColor);
+ SystemSetGroup(m_nCurGroup);
+ SystemUpdateAnimation(m_bAnimation, m_bAddKeys);
+ SystemUpdateRenderingMode((m_nDetail & LC_DET_BACKGROUND) != 0, (m_nDetail & LC_DET_FAST) != 0);
+ SystemUpdateSnap(m_nSnap);
+ SystemUpdateMoveSnap(m_nMoveSnap);
+ SystemUpdateCameraMenu(m_pCameras);
+ SystemUpdateCurrentCamera(NULL, m_pViewCameras[m_nActiveViewport], m_pCameras);
+ UpdateSelection();
+ if (m_bAnimation)
+ SystemUpdateTime(m_bAnimation, m_nCurFrame, m_nTotalFrames);
+ else
+ SystemUpdateTime(m_bAnimation, m_nCurStep, 255);
+ SystemRedrawView();
+
+ return true;
+}
+
+void Project::FileSave(File* file, bool bUndo)
+{
+ float ver_flt = 1.3f; // LeoCAD 0.70
+ unsigned long rgb;
+ unsigned char ch;
+ unsigned short sh;
+ int i, j;
+
+ file->Seek(0, SEEK_SET);
+ file->Write(LC_STR_VERSION, 32);
+ file->Write(&ver_flt, 4);
+
+ rgb = FLOATRGB(m_fBackground);
+ file->Write(&rgb, 4);
+
+ i = m_nAngleSnap; file->Write(&i, 4);
+ file->Write(&m_nSnap, 4);
+ file->Write(&m_fLineWidth, 4);
+ file->Write(&m_nDetail, 4);
+ i = m_nCurGroup; file->Write(&i, 4);
+ i = m_nCurColor; file->Write(&i, 4);
+ i = m_nCurAction; file->Write(&i, 4);
+ i = m_nCurStep; file->Write(&i, 4);
+ file->Write(&m_nScene, sizeof(m_nScene));
+
+ Piece* pPiece;
+ for (i = 0, pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ i++;
+ file->Write(&i, 4);
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ pPiece->FileSave(file, m_pGroups);
+
+ ch = strlen(m_strAuthor);
+ file->Write(&ch, 1);
+ file->Write(m_strAuthor, ch);
+ ch = strlen(m_strDescription);
+ file->Write(&ch, 1);
+ file->Write(m_strDescription, ch);
+ ch = strlen(m_strComments);
+ file->Write(&ch, 1);
+ file->Write(m_strComments, ch);
+
+ Group* pGroup;
+ for (i = 0, pGroup = m_pGroups; pGroup; pGroup = pGroup->m_pNext)
+ i++;
+ file->Write(&i, 4);
+
+ for (pGroup = m_pGroups; pGroup; pGroup = pGroup->m_pNext)
+ pGroup->FileSave(file, m_pGroups);
+
+ file->Write(&m_nViewportMode, 1);
+ file->Write(&m_nActiveViewport, 1);
+
+ Camera* pCamera;
+ for (i = 0, pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ i++;
+ file->Write(&i, 4);
+
+ for (i = 0, pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ pCamera->FileSave(file);
+
+ for (j = 0; j < 4; j++)
+ {
+ for (i = 0, pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ if (pCamera == m_pViewCameras[j])
+ break;
+ else
+ i++;
+
+ file->Write(&i, 4);
+ }
+
+ rgb = FLOATRGB(m_fFogColor);
+ file->Write(&rgb, 4);
+ file->Write(&m_fFogDensity, 4);
+ sh = strlen(m_strBackground);
+ file->Write(&sh, 2);
+ file->Write(m_strBackground, sh);
+ ch = strlen(m_strHeader);
+ file->Write(&ch, 1);
+ file->Write(m_strHeader, ch);
+ ch = strlen(m_strFooter);
+ file->Write(&ch, 1);
+ file->Write(m_strFooter, ch);
+ // 0.60 (1.0)
+ rgb = FLOATRGB(m_fAmbient);
+ file->Write(&rgb, 4);
+ ch = m_bAnimation;
+ file->Write(&ch, 1);
+ ch = m_bAddKeys;
+ file->Write(&ch, 1);
+ file->Write(&m_nFPS, 1);
+ file->Write(&m_nCurFrame, 2);
+ file->Write(&m_nTotalFrames, 2);
+ file->Write(&m_nGridSize, 2);
+ file->Write(&m_nMoveSnap, 2);
+ // 0.62 (1.1)
+ rgb = FLOATRGB(m_fGradient1);
+ file->Write(&rgb, 4);
+ rgb = FLOATRGB(m_fGradient2);
+ file->Write(&rgb, 4);
+ // 0.64 (1.2)
+ m_pTerrain->FileSave(file);
+
+ if (!bUndo)
+ {
+ unsigned long pos = 0;
+
+ i = SystemGetProfileInt("Default", "Save Preview", 0);
+ if (i != 0)
+ {
+ pos = file->GetPosition();
+
+ LC_IMAGE* image;
+ LC_IMAGE_OPTS opts;
+ opts.interlaced = false;
+ opts.transparent = false;
+ opts.format = LC_IMAGE_GIF;
+
+ i = m_bAnimation ? m_nCurFrame : m_nCurStep;
+ CreateImages(&image, 120, 100, i, i, false);
+ SaveImage(file, image, &opts);
+ free(image);
+ }
+
+ file->Write(&pos, 4);
+ m_nSaveTimer = 0;
+ }
+}
+
+void Project::FileReadLDraw(File* file, Matrix* prevmat, int* nOk, int DefColor, int* nStep)
+{
+ char buf[256];
+
+ while (file->ReadString(buf, 256))
+ {
+ strupr(buf);
+ if (strstr(buf, "STEP"))
+ {
+ (*nStep)++;
+ continue;
+ }
+
+ bool read = true;
+ char *ptr, tmp[LC_MAXPATH], pn[LC_MAXPATH];
+ int color, cmd;
+ float fmat[12];
+
+ if (sscanf(buf, "%d %d %g %g %g %g %g %g %g %g %g %g %g %g %s[12]",
+ &cmd, &color, &fmat[0], &fmat[1], &fmat[2], &fmat[3], &fmat[4], &fmat[5], &fmat[6],
+ &fmat[7], &fmat[8], &fmat[9], &fmat[10], &fmat[11], &tmp[0]) != 15)
+ continue;
+
+ Matrix incmat, tmpmat;
+ incmat.ConvertFromLDraw(fmat);
+ tmpmat.Multiply(*prevmat, incmat);
+
+ if (cmd == 1)
+ {
+ int cl = 0;
+ if (color == 16)
+ cl = DefColor;
+ else
+ cl = ConvertColor(color);
+
+ strcpy(pn, tmp);
+ ptr = strchr(tmp, '.');
+
+ if (ptr != NULL)
+ *ptr = 0;
+
+ if (strlen(tmp) < 9)
+ {
+ char name[9];
+ strcpy(name, tmp);
+
+ PieceInfo* pInfo = FindPieceInfo(name);
+ if (pInfo != NULL)
+ {
+ float x, y, z, rot[4];
+ Piece* pPiece = new Piece(pInfo);
+ read = false;
+
+ tmpmat.GetTranslation(&x, &y, &z);
+ pPiece->Initialize(x, y, z, *nStep, 1, cl);
+ pPiece->CreateName(m_pPieces);
+ AddPiece(pPiece);
+ tmpmat.ToAxisAngle(rot);
+ pPiece->ChangeKey(1, false, false, rot, PK_ROTATION);
+ pPiece->ChangeKey(1, true, false, rot, PK_ROTATION);
+ SystemPieceComboAdd(pInfo->m_strDescription);
+ (*nOk)++;
+ }
+ }
+
+ if (read)
+ {
+ File tf(false);
+ if (tf.Open(pn, "rt"))
+ FileReadLDraw(&tf, &tmpmat, nOk, cl, nStep);
+ }
+ }
+ }
+}
+
+bool Project::DoFileSave()
+{
+/*
+ DWORD dwAttrib = GetFileAttributes(m_strPathName);
+ if (dwAttrib & FILE_ATTRIBUTE_READONLY)
+ {
+ // we do not have read-write access or the file does not (now) exist
+ if (!DoSave(NULL, true))
+ return false;
+ }
+ else
+*/ {
+ if (!DoSave(m_strPathName, true))
+ return false;
+ }
+ return true;
+}
+
+// Save the document data to a file
+// lpszPathName = path name where to save document file
+// if lpszPathName is NULL then the user will be prompted (SaveAs)
+// note: lpszPathName can be different than 'm_strPathName'
+// if 'bReplace' is TRUE will change file name if successful (SaveAs)
+// if 'bReplace' is FALSE will not change path name (SaveCopyAs)
+bool Project::DoSave(char* lpszPathName, bool bReplace)
+{
+ File file(false);
+ char newName[LC_MAXPATH];
+ memset(newName, 0, sizeof(newName));
+ if (lpszPathName)
+ strcpy(newName, lpszPathName);
+
+ char ext[4], *ptr;
+ memset(ext, 0, 4);
+ ptr = strrchr(newName, '.');
+ if (ptr != NULL)
+ {
+ ptr++;
+ strncpy(ext, ptr, 3);
+ strlwr(ext);
+
+ if (strcmp(ext, "dat") == 0)
+ {
+ *ptr = 0;
+ strcat(newName, "lcd");
+ }
+ }
+
+ if (strlen(newName) == 0)
+ {
+ strcpy(newName, m_strPathName);
+ if (bReplace && strlen(newName) == 0)
+ {
+ strcpy(newName, m_strTitle);
+
+ // check for dubious filename
+ int iBad = strcspn(newName, " #%;/\\");
+ if (iBad != -1)
+ newName[iBad] = 0;
+
+ strcpy(newName, ".lcd");
+ }
+
+ if (!SystemDoDialog(LC_DLG_FILE_SAVE, &newName))
+ return false; // don't even attempt to save
+ }
+
+// CWaitCursor wait;
+
+ if (!file.Open(newName, "wb"))
+ {
+// MessageBox("Failed to save.");
+
+ // be sure to delete the file
+ if (lpszPathName == NULL)
+ remove(newName);
+
+ return false;
+ }
+
+ memset(ext, 0, 4);
+ ptr = strrchr(newName, '.');
+ if (ptr != NULL)
+ {
+ strncpy(ext, ptr+1, 3);
+ strlwr(ext);
+ }
+
+ if (strcmp(ext, "dat") == 0)
+ {
+ const int col[28] = { 4,12,2,10,1,9,14,15,8,0,6,13,13,334,36,44,34,42,33,41,46,47,7,382,6,13,11,383 };
+ Piece* pPiece;
+ int i, steps = GetLastStep();
+ char buf[256], *ptr;
+
+ ptr = strrchr(m_strPathName, '\\');
+ if (ptr == NULL)
+ ptr = strrchr(m_strPathName, '/');
+ if (ptr == NULL)
+ ptr = m_strPathName;
+ else
+ ptr++;
+
+ sprintf(buf, "0 Model exported from LeoCAD\r\n"
+ "0 Original name: %s\r\n", ptr);
+ if (strlen(m_strAuthor) != 0)
+ {
+ strcat(buf, "0 Author: ");
+ strcat(buf, m_strAuthor);
+ strcat(buf, "\r\n");
+ }
+ strcat(buf, "\r\n");
+ file.Write(buf, strlen(buf));
+
+ for (i = 1; i <= steps; i++)
+ {
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ if ((pPiece->IsVisible(i, false)) && (pPiece->GetStepShow() == i))
+ {
+ float f[12], position[3], rotation[4];
+ pPiece->GetPosition(position);
+ pPiece->GetRotation(rotation);
+ Matrix mat(rotation, position);
+ mat.ConvertToLDraw(f);
+ sprintf (buf, " 1 %d %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %s.DAT\r\n",
+ col[pPiece->GetColor()], f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9], f[10], f[11], pPiece->GetPieceInfo()->m_strName);
+ file.Write(buf, strlen(buf));
+ }
+ }
+
+ if (i != steps)
+ file.Write("0 STEP\r\n", 8);
+ }
+ file.Write("0\r\n", 3);
+ }
+ else
+ FileSave(&file, false); // save me
+ file.Close();
+
+ SetModifiedFlag(false); // back to unmodified
+
+ // reset the title and change the document name
+ if (bReplace)
+ SetPathName(newName, true);
+
+ return true; // success
+}
+
+// return true if ok to continue
+bool Project::SaveModified()
+{
+ if (!IsModified())
+ return true; // ok to continue
+
+ // get name/title of document
+ char name[LC_MAXPATH];
+ if (strlen(m_strPathName) == 0)
+ {
+ // get name based on caption
+ strcpy(name, m_strTitle);
+ if (strlen(name) == 0)
+ strcpy(name, "Untitled");
+ }
+ else
+ {
+ // get name based on file title of path name
+ char* p;
+
+ p = strrchr(m_strPathName, '\\');
+ if (!p)
+ p = strrchr(m_strPathName, '/');
+
+ if (p)
+ strcpy(name, ++p);
+ else
+ strcpy(name, m_strPathName);
+ }
+
+ char prompt[512];
+ sprintf(prompt, "Save changes to %s ?", name);
+
+ switch (SystemDoMessageBox(prompt, LC_MB_YESNOCANCEL))
+ {
+ case LC_CANCEL:
+ return false; // don't continue
+
+ case LC_YES:
+ // If so, either Save or Update, as appropriate
+ if (!DoFileSave())
+ return false; // don't continue
+ break;
+
+ case LC_NO:
+ // If not saving changes, revert the document
+ break;
+ }
+
+ return true; // keep going
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// File operations
+
+bool Project::OnNewDocument()
+{
+ SetTitle("Untitled");
+ DeleteContents(false);
+ memset(m_strPathName, 0, sizeof(m_strPathName)); // no path name yet
+ SetModifiedFlag(false); // make clean
+ LoadDefaults(true);
+ CheckPoint("");
+
+ SystemUpdateRecentMenu(m_strRecentFiles);
+ SystemUpdateFocus(NULL, LC_PIECE|LC_UPDATE_OBJECT|LC_UPDATE_TYPE);
+
+// CWnd* pFrame = AfxGetMainWnd();
+// if (pFrame != NULL)
+// pFrame->PostMessage (WM_LC_UPDATE_LIST, 1, m_nCurColor+1);
+// set cur group to 0
+
+ return true;
+}
+
+bool Project::OnOpenDocument(char* lpszPathName)
+{
+ File file(false);
+ bool bSuccess = false;
+
+ if (!file.Open(lpszPathName, "rb"))
+ {
+// MessageBox("Failed to open file.");
+ return false;
+ }
+
+ bool datfile = false;
+ char ext[4], *ptr;
+ memset(ext, 0, 4);
+ ptr = strrchr(lpszPathName, '.');
+ if (ptr != NULL)
+ {
+ strncpy(ext, ptr+1, 3);
+ strlwr(ext);
+ }
+
+ if (strcmp(ext, "dat") == 0)
+ datfile = true;
+
+ DeleteContents(false);
+ LoadDefaults(datfile);
+ SetModifiedFlag(true); // dirty during loading
+
+ SystemDoWaitCursor(1);
+ if (file.GetLength() != 0)
+ {
+ if (datfile)
+ {
+ int ok = 0, step = 1;
+ Matrix mat;
+ FileReadLDraw(&file, &mat, &ok, m_nCurColor, &step);
+ m_nCurStep = step;
+ SystemUpdateTime(false, m_nCurStep, 255);
+ SystemUpdateFocus(NULL, LC_PIECE|LC_UPDATE_TYPE|LC_UPDATE_OBJECT);
+ UpdateSelection();
+ CalculateStep();
+ SystemRedrawView();
+
+ char msg[50];
+ sprintf(msg, "%d objects imported.", ok);
+// AfxMessageBox(msg, MB_OK|MB_ICONINFORMATION);
+ bSuccess = true;
+ }
+ else
+ bSuccess = FileLoad(&file, false, false); // load me
+
+ CheckPoint("");
+ m_nSaveTimer = 0;
+ }
+ file.Close();
+ SystemDoWaitCursor(-1);
+
+ if (bSuccess == false)
+ {
+// MessageBox("Failed to load.");
+ DeleteContents(false); // remove failed contents
+ return false;
+ }
+
+ SetModifiedFlag(false); // start off with unmodified
+
+ return true;
+}
+
+void Project::SetPathName(char* lpszPathName, bool bAddToMRU)
+{
+ strcpy(m_strPathName, lpszPathName);
+
+ // always capture the complete file name including extension (if present)
+ char* lpszTemp = lpszPathName;
+ for (char* lpsz = lpszPathName; *lpsz != '\0'; lpsz++)
+ {
+ // remember last directory/drive separator
+ if (*lpsz == '\\' || *lpsz == '/' || *lpsz == ':')
+ lpszTemp = lpsz + 1;
+ }
+
+ // set the document title based on path name
+ SetTitle(lpszTemp);
+
+ // add it to the file MRU list
+ if (bAddToMRU)
+ {
+ // update the MRU list, if an existing MRU string matches file name
+ int i;
+ for (i = 0; i < 3; i++)
+ {
+ if (strcmp(m_strRecentFiles[i], lpszPathName) == 0)
+ break;
+ }
+
+ // move MRU strings before this one down
+ for (; i > 0; i--)
+ strcpy(m_strRecentFiles[i], m_strRecentFiles[i-1]);
+ strcpy(m_strRecentFiles[0], lpszPathName);
+ SystemUpdateRecentMenu(m_strRecentFiles);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Undo/Redo support
+
+// Save current state.
+void Project::CheckPoint(char* text)
+{
+ UNDOINFO* pTmp;
+ UNDOINFO* pUndo = new UNDOINFO;
+ int i;
+
+ strcpy(pUndo->strText, text);
+ FileSave(&pUndo->file, true);
+
+ for (pTmp = m_pUndoList, i = 0; pTmp; pTmp = pTmp->pNext, i++)
+ if ((i == 30) && (pTmp->pNext != NULL))
+ {
+ delete pTmp->pNext;
+ pTmp->pNext = NULL;
+ m_bUndoOriginal = false;
+ }
+
+ pUndo->pNext = m_pUndoList;
+ m_pUndoList = pUndo;
+
+ while (m_pRedoList)
+ {
+ pUndo = m_pRedoList;
+ m_pRedoList = m_pRedoList->pNext;
+ delete pUndo;
+ }
+ m_pRedoList = NULL;
+
+ SystemUpdateUndoRedo(m_pUndoList->pNext ? m_pUndoList->strText : NULL, NULL);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Project rendering
+
+// Only this function should be called.
+void Project::Render(bool bToMemory)
+{
+ if (bToMemory)
+ {
+// m_bDenyRender = TRUE;
+// m_bRendering = TRUE;
+ RenderScene(true, false);
+// m_bRendering = FALSE;
+// m_bDenyRender = FALSE;
+ return;
+ }
+
+// if (m_bDenyRender)
+// return;
+
+#ifdef _DEBUG
+#ifdef _WINDOWS
+#define BENCHMARK
+#endif
+#endif
+
+#ifdef BENCHMARK
+ DWORD dwMillis = GetTickCount();
+#endif
+
+ m_bStopRender = false;
+ m_bRendering = true;
+ RenderScene((m_nDetail & LC_DET_FAST) == 0, true);
+ SystemSwapBuffers();
+
+ if ((m_nDetail & LC_DET_FAST) && (m_nDetail & LC_DET_BACKGROUND))
+ {
+ RenderScene(true, true);
+ if (!m_bStopRender)
+ SystemSwapBuffers();
+ }
+ m_bRendering = false;
+
+
+#ifdef BENCHMARK
+ dwMillis = GetTickCount() - dwMillis;
+ char szMsg[30];
+ sprintf(szMsg, "%.4f sec", (float)dwMillis/1000);
+ AfxGetMainWnd()->SetWindowText(szMsg);
+#endif
+}
+
+typedef struct LC_BSPNODE
+{
+ float plane[4];
+ Piece* piece;
+ LC_BSPNODE* front;
+ LC_BSPNODE* back;
+
+ ~LC_BSPNODE()
+ {
+ if (piece == NULL)
+ {
+ if (front)
+ delete front;
+ if (back)
+ delete back;
+ }
+ }
+} LC_BSPNODE;
+
+static void RenderBSP(LC_BSPNODE* node, float* eye, bool* bSel,
+ bool bLighting, bool bNoAlpha, bool bEdges, unsigned char* nLastColor, bool* bTrans)
+{
+ if (node->piece)
+ {
+ if (node->piece->IsSelected())
+ {
+ if (!*bSel)
+ {
+ *bSel = true;
+ glLineWidth (2);//*m_fLineWidth);
+ }
+ }
+ else
+ {
+ if (*bSel)
+ {
+ *bSel = false;
+ glLineWidth(1);//m_fLineWidth);
+ }
+ }
+
+ node->piece->Render(bLighting, bNoAlpha, bEdges, nLastColor, bTrans);
+ return;
+ }
+
+ if (eye[0]*node->plane[0] + eye[1]*node->plane[1] +
+ eye[2]*node->plane[2] + node->plane[3] > 0.0f)
+ {
+ RenderBSP(node->back, eye, bSel, bLighting, bNoAlpha, bEdges, nLastColor, bTrans);
+ RenderBSP(node->front, eye, bSel, bLighting, bNoAlpha, bEdges, nLastColor, bTrans);
+ }
+ else
+ {
+ RenderBSP(node->front, eye, bSel, bLighting, bNoAlpha, bEdges, nLastColor, bTrans);
+ RenderBSP(node->back, eye, bSel, bLighting, bNoAlpha, bEdges, nLastColor, bTrans);
+ }
+}
+
+static void BuildBSP(LC_BSPNODE* node, Piece* pList)
+{
+ Piece *front_list = NULL, *back_list = NULL;
+ Piece *pPiece, *pNext;
+
+ node->piece = NULL;
+
+ if (pList->m_pLink == NULL)
+ {
+ // This is a leaf
+ node->piece = pList;
+ return;
+ }
+
+ float dx, dy, dz, bs[6] = { 10000, 10000, 10000, -10000, -10000, -10000 };
+ const float *pos;
+
+ for (pPiece = pList; pPiece; pPiece = pPiece->m_pLink)
+ {
+ pos = pPiece->GetConstPosition();
+ if (pos[0] < bs[0]) bs[0] = pos[0];
+ if (pos[1] < bs[1]) bs[1] = pos[1];
+ if (pos[2] < bs[2]) bs[2] = pos[2];
+ if (pos[0] > bs[3]) bs[3] = pos[0];
+ if (pos[1] > bs[4]) bs[4] = pos[1];
+ if (pos[2] > bs[5]) bs[5] = pos[2];
+ }
+
+ dx = ABS(bs[0]-bs[3]);
+ dy = ABS(bs[1]-bs[4]);
+ dz = ABS(bs[2]-bs[5]);
+
+ node->plane[0] = node->plane[1] = node->plane[2] = 0.0f;
+
+ if (dx > dy)
+ {
+ if (dx > dz)
+ node->plane[0] = 1.0f;
+ else
+ node->plane[2] = 1.0f;
+ }
+ else
+ {
+ if (dy > dz)
+ node->plane[1] = 1.0f;
+ else
+ node->plane[2] = 1.0f;
+ }
+
+ // D = -Ax -By -Cz
+ node->plane[3] = -(node->plane[0]*(bs[0]+bs[3])/2)-(node->plane[1]*(bs[1]+bs[4])/2)-(node->plane[2]*(bs[2]+bs[5])/2);
+
+ for (pPiece = pList; pPiece;)
+ {
+ pos = pPiece->GetConstPosition();
+ pNext = pPiece->m_pLink;
+
+ if (pos[0]*node->plane[0] + pos[1]*node->plane[1] +
+ pos[2]*node->plane[2] + node->plane[3] > 0.0f)
+ {
+ pPiece->m_pLink = front_list;
+ front_list = pPiece;
+ }
+ else
+ {
+ pPiece->m_pLink = back_list;
+ back_list = pPiece;
+ }
+
+ pPiece = pNext;
+ }
+
+ if (bs[0] == bs[3] && bs[1] == bs[4] && bs[2] == bs[5])
+ {
+ if (back_list)
+ {
+ front_list = back_list;
+ back_list = back_list->m_pLink;
+ front_list->m_pLink = NULL;
+ }
+ else
+ {
+ back_list = front_list;
+ front_list = front_list->m_pLink;
+ back_list->m_pLink = NULL;
+ }
+ }
+
+ if (front_list)
+ {
+ node->front = new LC_BSPNODE;
+ BuildBSP(node->front, front_list);
+ }
+ else
+ node->front = NULL;
+
+ if (back_list)
+ {
+ node->back = new LC_BSPNODE;
+ BuildBSP(node->back, back_list);
+ }
+ else
+ node->back = NULL;
+}
+
+void Project::RenderScene(bool bShaded, bool bDrawViewports)
+{
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ if (bDrawViewports)
+ {
+ if (bShaded)
+ {
+ if (m_nDetail & LC_DET_LIGHTING)
+ glDisable (GL_LIGHTING);
+ if (m_nScene & LC_SCENE_FOG)
+ glDisable (GL_FOG);
+ RenderViewports(true, true);
+ if (m_nDetail & LC_DET_LIGHTING)
+ glEnable(GL_LIGHTING);
+ if (m_nScene & LC_SCENE_FOG)
+ glEnable (GL_FOG);
+ }
+ else
+ RenderViewports(true, true);
+ }
+ else
+ RenderViewports(true, false);
+
+/*
+// if ((m_dwDetail & DET_LIGHTING) != 0)
+ for (POSITION pos = m_Lights.GetHeadPosition(); pos != NULL;)
+ {
+ CLight* pLight = m_Lights.GetNext(pos);
+
+// pLight->LoadPosition();
+ glLightfv(GL_LIGHT0, GL_POSITION, pLight->m_fPosition);
+
+float one[] = {1.f, 1.f, 1.f, 1.f};
+glEnable(GL_LIGHT0);
+// glLightfv(GL_LIGHT0, GL_AMBIENT, one);
+glLightfv(GL_LIGHT0, GL_SPECULAR, one);
+
+ }
+*/
+ for (int vp = 0; vp < viewports[m_nViewportMode].n; vp++)
+ {
+ int x = (int)(viewports[m_nViewportMode].dim[vp][0] * ((float)m_nViewX));
+ int y = (int)(viewports[m_nViewportMode].dim[vp][1] * ((float)m_nViewY));
+ int w = (int)(viewports[m_nViewportMode].dim[vp][2] * ((float)m_nViewX));
+ int h = (int)(viewports[m_nViewportMode].dim[vp][3] * ((float)m_nViewY));
+
+ float ratio = (float)w/h;
+ glViewport(x, y, w, h);
+ m_pViewCameras[vp]->LoadProjection(ratio);
+
+ if ((m_nSnap & LC_DRAW_AXIS) || (m_nSnap & LC_DRAW_GRID))
+ {
+ if ((bShaded) && (m_nDetail & LC_DET_LIGHTING))
+ glDisable(GL_LIGHTING);
+
+ glColor3f(1.0f - m_fBackground[0], 1.0f - m_fBackground[1], 1.0f - m_fBackground[2]);
+
+ // There's got to be an easier way...
+ if (m_nSnap & LC_DRAW_AXIS)
+ {
+ double model[16], proj[16], obj1x, obj1y, obj1z, obj2x, obj2y, obj2z;
+ int viewport[4];
+
+ glGetDoublev(GL_MODELVIEW_MATRIX, model);
+ glGetDoublev(GL_PROJECTION_MATRIX, proj);
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ gluUnProject(25, 25, 0.1, model, proj, viewport, &obj1x, &obj1y, &obj1z);
+ gluUnProject(45, 25, 0.1, model, proj, viewport, &obj2x, &obj2y, &obj2z);
+
+ float ds = (float)sqrt((obj1x-obj2x)*(obj1x-obj2x)+(obj1y-obj2y)*(obj1y-obj2y)+(obj1z-obj2z)*(obj1z-obj2z));
+ float verts[30][3] = { {0,0,0}, {0.8f*ds,0,0}, {0.8f*ds,0.2f*ds,0}, {ds,0,0}, {0.8f*ds,-0.2f*ds,0},
+ {0.8f*ds,0,0}, {0.8f*ds,0,0.2f*ds}, {ds,0,0}, {0.8f*ds,0,-0.2f*ds}, {0.8f*ds,0,0},
+ {0,0,0},{0,0.8f*ds,0}, {0,0.8f*ds,0.2f*ds}, {0,ds,0}, {0,0.8f*ds,-0.2f*ds},
+ {0,0.8f*ds,0}, {0.2f*ds,0.8f*ds,0}, {0,ds,0}, {-0.2f*ds,0.8f*ds,0}, {0,0.8f*ds,0},
+ {0,0,0}, {0,0,0.8f*ds}, {0.2f*ds,0,0.8f*ds}, {0,0,ds}, {-0.2f*ds,0,0.8f*ds},
+ {0,0,0.8f*ds}, {0,0.2f*ds,0.8f*ds}, {0,0,ds}, {0,-0.2f*ds,0.8f*ds}, {0,0,0.8f*ds} };
+
+ glPushMatrix();
+ glTranslated(obj1x, obj1y, obj1z);
+ glVertexPointer (3, GL_FLOAT, 0, verts);
+ glDrawArrays(GL_LINE_STRIP, 0, 30);
+
+ Matrix m;
+ m.FromInverse(model);
+ m.SetTranslation(0,0,0);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ m_pTextures[0].MakeCurrent();
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_ALPHA_TEST);
+
+ TXFVERT* glyph;
+
+ glPushMatrix();
+ glTranslatef(1.4f*ds, 0, 0);
+ glMultMatrixf(m.m);
+ glyph = &glyphs['X'-32];
+ glBegin(GL_QUADS);
+ glTexCoord2f(glyph->left, glyph->top);
+ glVertex2f(0, 0.4f*ds);
+ glTexCoord2f(glyph->left, glyph->bottom);
+ glVertex2f(0, -0.4f*ds);
+ glTexCoord2f(glyph->right, glyph->bottom);
+ glVertex2f(glyph->width*ds/20, -0.4f*ds);
+ glTexCoord2f(glyph->right, glyph->top);
+ glVertex2f(glyph->width*ds/20, 0.4f*ds);
+ glEnd();
+ glPopMatrix();
+
+ glPushMatrix();
+ glTranslatef(0, 1.4f*ds, 0);
+ glMultMatrixf(m.m);
+ glyph = &glyphs['Y'-32];
+ glBegin(GL_QUADS);
+ glTexCoord2f(glyph->left, glyph->top);
+ glVertex2f(0, 0.4f*ds);
+ glTexCoord2f(glyph->left, glyph->bottom);
+ glVertex2f(0, -0.4f*ds);
+ glTexCoord2f(glyph->right, glyph->bottom);
+ glVertex2f(glyph->width*ds/20, -0.4f*ds);
+ glTexCoord2f(glyph->right, glyph->top);
+ glVertex2f(glyph->width*ds/20, 0.4f*ds);
+ glEnd();
+ glPopMatrix();
+
+ glPushMatrix();
+ glTranslatef(0, 0, 1.4f*ds);
+ glMultMatrixf(m.m);
+ glyph = &glyphs['Z'-32];
+ glBegin(GL_QUADS);
+ glTexCoord2f(glyph->left, glyph->top);
+ glVertex2f(0, 0.4f*ds);
+ glTexCoord2f(glyph->left, glyph->bottom);
+ glVertex2f(0, -0.4f*ds);
+ glTexCoord2f(glyph->right, glyph->bottom);
+ glVertex2f(glyph->width*ds/20, -0.4f*ds);
+ glTexCoord2f(glyph->right, glyph->top);
+ glVertex2f(glyph->width*ds/20, 0.4f*ds);
+ glEnd();
+ glPopMatrix();
+
+ glPopMatrix();
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_ALPHA_TEST);
+ }
+
+ if (m_nSnap & LC_DRAW_GRID)
+ glCallList (m_nGridList);
+
+ if ((bShaded) && (m_nDetail & LC_DET_LIGHTING))
+ glEnable(GL_LIGHTING);
+ }
+
+ if (bShaded)
+ {
+ if (m_nScene & LC_SCENE_FLOOR)
+ m_pTerrain->Render(m_pViewCameras[vp], ratio);
+
+ unsigned char nLastColor = 255;
+ bool bTrans = false;
+ bool bSel = false;
+ bool bCull = false;
+ Piece* pPiece;
+
+ LC_BSPNODE tree;
+ tree.front = tree.back = NULL;
+ Piece* pList = NULL;
+
+ // Draw opaque pieces first
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ if (m_nDetail & LC_DET_BACKGROUND)
+ {
+ SystemPumpMessages();
+ if (m_bStopRender)
+ return;
+ }
+
+ if (pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation))
+ {
+ if (!pPiece->IsTransparent())
+ {
+ if (pPiece->IsSelected())
+ {
+ if (!bSel)
+ {
+ bSel = true;
+ glLineWidth (2*m_fLineWidth);
+ }
+ }
+ else
+ {
+ if (bSel)
+ {
+ bSel = false;
+ glLineWidth(m_fLineWidth);
+ }
+ }
+/*
+ if (pPiece->m_pInfo->m_nConnectionCount == 1)
+ {
+ if (bCull)
+ {
+ bCull = false;
+ glDisable(GL_CULL_FACE);
+ }
+ }
+ else
+ {
+ if (!bCull)
+ {
+ bCull = true;
+ glEnable(GL_CULL_FACE);
+ }
+ }
+*/
+ pPiece->Render((m_nDetail & LC_DET_LIGHTING) != 0, (m_nDetail & LC_DET_SCREENDOOR) != 0, (m_nDetail & LC_DET_BRICKEDGES) != 0, &nLastColor, &bTrans);
+ }
+ else
+ {
+ pPiece->m_pLink = pList;
+ pList = pPiece;
+ }
+ }
+ }
+
+ if (pList)
+ {
+ float eye[3];
+ m_pViewCameras[vp]->GetEye(eye);
+ BuildBSP(&tree, pList);
+ RenderBSP(&tree, eye, &bSel,
+ (m_nDetail & LC_DET_LIGHTING) != 0, (m_nDetail & LC_DET_SCREENDOOR) != 0, (m_nDetail & LC_DET_BRICKEDGES) != 0, &nLastColor, &bTrans);
+ }
+
+
+#if 0
+ // Draw transparent pieces
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+// MSG msg;
+// while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+// {
+// TranslateMessage(&msg);
+// DispatchMessage(&msg);
+// if (m_bStopRender)
+// return;
+// }
+
+ if ((pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation)) &&
+ (pPiece->IsTransparent()))
+ {
+ if (pPiece->IsSelected())
+ {
+ if (!bSel)
+ {
+ bSel = true;
+ glLineWidth (2*m_fLineWidth);
+ }
+ }
+ else
+ {
+ if (bSel)
+ {
+ bSel = false;
+ glLineWidth(m_fLineWidth);
+ }
+ }
+/*
+ if (pPiece->m_pInfo->m_nConnectionCount == 1)
+ {
+ if (bCull)
+ {
+ bCull = FALSE;
+ glDisable(GL_CULL_FACE);
+ }
+ }
+ else
+ {
+ if (!bCull)
+ {
+ bCull = TRUE;
+ glEnable(GL_CULL_FACE);
+ }
+ }
+*/
+ pPiece->Render((m_nDetail & LC_DET_LIGHTING) != 0, (m_nDetail & LC_DET_SCREENDOOR) != 0, (m_nDetail & LC_DET_BRICKEDGES) != 0, &nLastColor, &bTrans);
+ }
+ }
+#endif
+ if (bTrans)
+ {
+ if (m_nDetail & LC_DET_SCREENDOOR)
+ glDisable(GL_POLYGON_STIPPLE);
+ else
+ {
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+ }
+ }
+ if (bSel)
+ glLineWidth(m_fLineWidth);
+ if (bCull)
+ glDisable(GL_CULL_FACE);
+ }
+ else
+ {
+// glEnable (GL_CULL_FACE);
+// glShadeModel (GL_FLAT);
+// glDisable (GL_LIGHTING);
+ if ((m_nDetail & LC_DET_BOX_FILL) == 0)
+ {
+ if ((m_nDetail & LC_DET_HIDDEN_LINE) != 0)
+ {
+ // Wireframe with hidden lines removed
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ RenderBoxes(false);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ RenderBoxes(true);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
+ else
+ {
+ // Wireframe
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ RenderBoxes(true);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
+ }
+ else
+ RenderBoxes(true);
+ }
+
+ // Draw cameras & lights
+ if (bDrawViewports)
+ {
+ Camera* pCamera;
+
+ for (pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ {
+ if ((pCamera == m_pViewCameras[vp]) || !pCamera->IsVisible())
+ continue;
+ pCamera->Render(m_fLineWidth);
+ }
+
+ if ((m_nDetail & LC_DET_LIGHTING) && (m_pLights != NULL))
+ {
+// Light* pLight;
+
+ glDisable (GL_LIGHTING);
+ glColor3f(1, 1, 0);
+
+// for (pLight = m_pLights; pLight; pLight = pLight->m_pNext)
+// pLight->Render();
+
+ glEnable (GL_LIGHTING);
+ }
+ }
+ }
+}
+
+void Project::RenderViewports(bool bBackground, bool bLines)
+{
+ float x, y, w, h;
+ int vp;
+
+ glViewport(0, 0, m_nViewX, m_nViewY);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, m_nViewX, 0, m_nViewY, -1, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+// glTranslatef(0.375, 0.375, 0.0);
+
+ if (bBackground)
+ {
+ // Draw gradient
+ if (m_nScene & LC_SCENE_GRADIENT)
+ {
+ if ((m_nDetail & LC_DET_SMOOTH) == 0)
+ glShadeModel(GL_SMOOTH);
+ glDisable(GL_DEPTH_TEST);
+ glBegin(GL_QUADS);
+
+ for (vp = 0; vp < viewports[m_nViewportMode].n; vp++)
+ {
+ x = viewports[m_nViewportMode].dim[vp][0] * (float)m_nViewX;
+ y = viewports[m_nViewportMode].dim[vp][1] * (float)m_nViewY;
+ w = viewports[m_nViewportMode].dim[vp][2] * (float)m_nViewX;
+ h = viewports[m_nViewportMode].dim[vp][3] * (float)m_nViewY;
+
+ glColor3fv(m_fGradient1);
+ glVertex2f(x+w, y+h);
+ glVertex2f(x, y+h);
+ glColor3fv(m_fGradient2);
+ glVertex2f(x, y);
+ glVertex2f(x+w, y);
+ }
+ glEnd();
+ glEnable(GL_DEPTH_TEST);
+ if ((m_nDetail & LC_DET_SMOOTH) == 0)
+ glShadeModel(GL_FLAT);
+ }
+
+ glEnable(GL_TEXTURE_2D);
+
+ // Draw the background
+ if (m_nScene & LC_SCENE_BG)
+ {
+ glDisable (GL_DEPTH_TEST);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ m_pBackground->MakeCurrent();
+ glBegin(GL_QUADS);
+
+ for (vp = 0; vp < viewports[m_nViewportMode].n; vp++)
+ {
+ x = viewports[m_nViewportMode].dim[vp][0] * (float)m_nViewX;
+ y = viewports[m_nViewportMode].dim[vp][1] * (float)m_nViewY;
+ w = viewports[m_nViewportMode].dim[vp][2] * (float)m_nViewX;
+ h = viewports[m_nViewportMode].dim[vp][3] * (float)m_nViewY;
+
+ float tw = 1.0f, th = 1.0f;
+ if (m_nScene & LC_SCENE_BG_TILE)
+ {
+ tw = w/m_pBackground->m_nWidth;
+ th = h/m_pBackground->m_nHeight;
+ }
+
+ glTexCoord2f(0, 0);
+ glVertex2f(x, y+h);
+ glTexCoord2f(tw, 0);
+ glVertex2f(x+w, y+h);
+ glTexCoord2f(tw, th);
+ glVertex2f(x+w, y);
+ glTexCoord2f(0, th);
+ glVertex2f(x, y);
+ }
+ glEnd();
+ glEnable(GL_DEPTH_TEST);
+ }
+
+ if (!bLines)
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ if (bLines)
+ {
+ // Draw text
+ glColor3f(0, 0, 0);
+ if (!bBackground)
+ glEnable(GL_TEXTURE_2D);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ m_pTextures[0].MakeCurrent();
+ glEnable(GL_ALPHA_TEST);
+ glBegin(GL_QUADS);
+
+ for (vp = 0; vp < viewports[m_nViewportMode].n; vp++)
+ {
+ x = viewports[m_nViewportMode].dim[vp][0] * (float)m_nViewX;
+ y = viewports[m_nViewportMode].dim[vp][1] * (float)m_nViewY;
+ w = viewports[m_nViewportMode].dim[vp][2] * (float)m_nViewX;
+ h = viewports[m_nViewportMode].dim[vp][3] * (float)m_nViewY;
+
+ float l = x+3, t = y+h-6;
+ for (const char* p = m_pViewCameras[vp]->GetName(); *p; p++)
+ {
+ if (*p < 32 || *p > 125)
+ continue;
+ if (glyphs[*p-32].width == 0)
+ continue;
+
+ glTexCoord2f(glyphs[*p-32].left, glyphs[*p-32].top);
+ glVertex2f(l, t);
+ glTexCoord2f(glyphs[*p-32].left, glyphs[*p-32].bottom);
+ glVertex2f(l, t-16);
+ glTexCoord2f(glyphs[*p-32].right, glyphs[*p-32].bottom);
+ glVertex2f(l + glyphs[*p-32].width, t-16);
+ glTexCoord2f(glyphs[*p-32].right, glyphs[*p-32].top);
+ glVertex2f(l + glyphs[*p-32].width, t);
+ l += glyphs[*p-32].width;
+ }
+ }
+ glEnd();
+
+ glDisable(GL_ALPHA_TEST);
+ glDisable(GL_TEXTURE_2D);
+
+ // Borders
+ if (m_fLineWidth != 1.0f)
+ glLineWidth (1.0f);
+
+ for (vp = 0; vp < viewports[m_nViewportMode].n; vp++)
+ {
+ if (vp == m_nActiveViewport)
+ continue;
+
+ x = viewports[m_nViewportMode].dim[vp][0] * (float)m_nViewX;
+ y = viewports[m_nViewportMode].dim[vp][1] * (float)m_nViewY;
+ w = viewports[m_nViewportMode].dim[vp][2] * (float)m_nViewX;
+ h = viewports[m_nViewportMode].dim[vp][3] * (float)m_nViewY;
+
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(x, y);
+ glVertex2f(x+w, y);
+ glVertex2f(x+w, y+h);
+ glVertex2f(x, y+h);
+ glEnd();
+ }
+
+ x = viewports[m_nViewportMode].dim[m_nActiveViewport][0] * (float)m_nViewX;
+ y = viewports[m_nViewportMode].dim[m_nActiveViewport][1] * (float)m_nViewY;
+ w = viewports[m_nViewportMode].dim[m_nActiveViewport][2] * (float)m_nViewX;
+ h = viewports[m_nViewportMode].dim[m_nActiveViewport][3] * (float)m_nViewY;
+
+ glColor3f(1.0f, 0, 0);
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(x, y);
+ glVertex2f(x+w, y);
+ glVertex2f(x+w, y+h);
+ glVertex2f(x, y+h);
+ glEnd();
+
+ if (m_fLineWidth != 1.0f)
+ glLineWidth (m_fLineWidth);
+ }
+}
+
+// bHilite - Draws focus/selection, not used for the
+// first rendering pass if remove hidden lines is enabled
+void Project::RenderBoxes(bool bHilite)
+{
+ Piece* pPiece;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation))
+ pPiece->RenderBox(bHilite, m_fLineWidth);
+}
+
+// Initialize OpenGL
+void Project::RenderInitialize()
+{
+ unsigned long stipple_pattern[32];
+ int i;
+ for (i = 0; i < 32; i += 2)
+ {
+ stipple_pattern[i] = 0xAAAAAAAA;
+ stipple_pattern[i+1] = 0x55555555;
+ }
+ glLineStipple (1, 65280);
+ glPolygonStipple ((GLubyte*)&stipple_pattern[0]);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(0.5f, 0.1f);
+
+ glDrawBuffer(GL_BACK);
+ glCullFace(GL_BACK);
+ glDisable (GL_CULL_FACE);
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LEQUAL);
+ glDepthMask(GL_TRUE);
+
+ if (m_nDetail & LC_DET_DITHER)
+ glEnable(GL_DITHER);
+ else
+ glDisable(GL_DITHER);
+/*
+ // TODO: use a blending function
+ if (m_dwDetail & DET_ANTIALIAS)
+ {
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_POLYGON_SMOOTH);
+ }
+ else
+ {
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_POLYGON_SMOOTH);
+ }
+*/
+ if (m_nDetail & LC_DET_SMOOTH)
+ glShadeModel(GL_SMOOTH);
+ else
+ glShadeModel(GL_FLAT);
+
+ if (m_nDetail & LC_DET_LIGHTING)
+ {
+ glEnable(GL_LIGHTING);
+ glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
+ glEnable(GL_COLOR_MATERIAL);
+float spec[4] = { 0.3f, 0.3f, 0.3f, 1.0f };
+glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spec);
+glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 10);
+// call initlights()
+ }
+ else
+ {
+ glDisable(GL_LIGHTING);
+ glDisable(GL_COLOR_MATERIAL);
+ }
+
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT, m_fAmbient);
+ glClearColor(m_fBackground[0], m_fBackground[1], m_fBackground[2], 1.0f);
+
+ if (m_nScene & LC_SCENE_FOG)
+ {
+ glEnable(GL_FOG);
+ glFogi(GL_FOG_MODE, GL_EXP);
+ glFogf(GL_FOG_DENSITY, m_fFogDensity);
+ glFogfv(GL_FOG_COLOR, m_fFogColor);
+ }
+ else
+ glDisable (GL_FOG);
+
+ // Load font (always the first texture)
+ m_pTextures[0].Load(false);
+ glAlphaFunc(GL_GREATER, 0.0625);
+
+ if (m_nScene & LC_SCENE_FLOOR)
+ m_pTerrain->LoadTexture((m_nDetail & LC_DET_LINEAR) != 0);
+
+ if (m_nScene & LC_SCENE_BG)
+ if (!m_pBackground->LoadFromFile(m_strBackground, (m_nDetail & LC_DET_LINEAR) != 0))
+ {
+ m_nScene &= ~LC_SCENE_BG;
+// AfxMessageBox ("Could not load background");
+ }
+
+ // Set the perspective correction hint to fastest or nicest...
+// glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
+
+ // Grid display list
+ if (m_nGridList == 0)
+ m_nGridList = glGenLists(1);
+ glNewList (m_nGridList, GL_COMPILE);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ i = 2*(4*m_nGridSize+2); // verts needed (2*lines)
+ float *grid = (float*)malloc(i*sizeof(float[3]));
+ float x = m_nGridSize*0.8f;
+
+ for (int j = 0; j <= m_nGridSize*2; j++)
+ {
+ grid[j*12] = x;
+ grid[j*12+1] = m_nGridSize*0.8f;
+ grid[j*12+2] = 0;
+ grid[j*12+3] = x;
+ grid[j*12+4] = -m_nGridSize*0.8f;
+ grid[j*12+5] = 0;
+ grid[j*12+6] = m_nGridSize*0.8f;
+ grid[j*12+7] = x;
+ grid[j*12+8] = 0;
+ grid[j*12+9] = -m_nGridSize*0.8f;
+ grid[j*12+10] = x;
+ grid[j*12+11] = 0;
+ x -= 0.8f;
+ }
+ glVertexPointer(3, GL_FLOAT, 0, grid);
+ glDrawArrays(GL_LINES, 0, i);
+ glEndList();
+ free(grid);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Project functions
+
+void Project::AddPiece(Piece* pPiece)
+{
+/*
+// Sort piece array to avoid OpenGL state changes (needs work)
+void CCADDoc::AddPiece(CPiece* pNewPiece)
+{
+ POSITION pos1, pos2;
+
+ for (pos1 = m_Pieces.GetHeadPosition(); (pos2 = pos1) != NULL;)
+ {
+ CPiece* pPiece = m_Pieces.GetNext(pos1);
+ if (pPiece->IsTransparent())
+ break;
+ }
+
+ if (pos2 == NULL || pNewPiece->IsTransparent())
+ m_Pieces.AddTail(pNewPiece);
+ else
+ m_Pieces.InsertBefore(pos2, pNewPiece);
+}
+*/
+ if (m_pPieces != NULL)
+ {
+ pPiece->m_pNext = m_pPieces;
+ m_pPieces = pPiece;
+ // TODO: sorting and BSP
+ }
+ else
+ {
+ m_pPieces = pPiece;
+ }
+
+ pPiece->AddConnections(m_pConnections);
+}
+
+void Project::RemovePiece(Piece* pPiece)
+{
+ Piece* pTemp, *pLast;
+ pLast = NULL;
+
+ for (pTemp = m_pPieces; pTemp; pLast = pTemp, pTemp = pTemp->m_pNext)
+ if (pTemp == pPiece)
+ {
+ if (pLast != NULL)
+ pLast->m_pNext = pTemp->m_pNext;
+ else
+ m_pPieces = pTemp->m_pNext;
+
+ break;
+ }
+
+ pPiece->RemoveConnections(m_pConnections);
+
+ // TODO: remove from BSP
+}
+
+void Project::CalculateStep()
+{
+ Piece* pPiece;
+ Camera* pCamera;
+ Light* pLight;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ pPiece->UpdatePosition(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation);
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ pPiece->CalculateConnections(m_pConnections, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, false, false);
+
+ for (pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ pCamera->UpdatePosition(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation);
+
+ for (pLight = m_pLights; pLight; pLight = pLight->m_pNext)
+ pLight->UpdatePosition(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation);
+}
+
+// Returns true if anything was removed (used by cut and del)
+bool Project::RemoveSelectedObjects()
+{
+ Piece* pPiece;
+ Camera* pCamera;
+ Light* pLight;
+ void* pPrev;
+ bool removed = false;
+
+ pPiece = m_pPieces;
+ while (pPiece)
+ {
+ if (pPiece->IsSelected())
+ {
+ Piece* pTemp;
+ pTemp = pPiece->m_pNext;
+
+ removed = true;
+ RemovePiece(pPiece);
+ delete pPiece;
+ pPiece = pTemp;
+ }
+ else
+ pPiece = pPiece->m_pNext;
+ }
+
+ // Cameras can't be removed while being used or default
+ for (pCamera = m_pCameras; pCamera; pPrev = pCamera, pCamera = pCamera->m_pNext)
+ {
+ if (pCamera->IsSelected() && pCamera->IsUser())
+ {
+ bool bCanDelete = true;
+ for (int i = 0; i < 4; i++)
+ if (pCamera == m_pViewCameras[i])
+ {
+ bCanDelete = false;
+ break;
+ }
+
+ if (bCanDelete)
+ {
+ ((Camera*)pPrev)->m_pNext = pCamera->m_pNext;
+ delete pCamera;
+ pCamera = (Camera*)pPrev;
+ removed = true;
+ SystemUpdateCameraMenu(m_pCameras);
+ SystemUpdateCurrentCamera(NULL, m_pViewCameras[m_nActiveViewport], m_pCameras);
+ }
+ }
+ }
+
+ for (pLight = m_pLights; pLight; pLight = pLight->m_pNext)
+ {
+/*
+ CLight* pLight = m_Lights.GetNext(pos1);
+ if (pLight->m_bSelected || pLight->m_bTargetSelected)
+ {
+ CLight* tmp = m_Lights.GetAt(pos2);
+ m_Lights.RemoveAt(pos2);
+ delete tmp;
+ ret = TRUE;
+ }
+*/
+ }
+
+ RemoveEmptyGroups();
+// CalculateStep();
+// AfxGetMainWnd()->PostMessage(WM_LC_UPDATE_INFO, NULL, OT_PIECE);
+
+ return removed;
+}
+
+void Project::UpdateSelection()
+{
+ unsigned long flags = 0;
+
+ if (m_pPieces == NULL)
+ flags |= LC_SEL_NO_PIECES;
+ else
+ {
+ Piece* pPiece;
+ Group* pGroup = NULL;
+ bool first = true;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ if (pPiece->IsSelected())
+ {
+ if (flags & LC_SEL_PIECE)
+ flags |= LC_SEL_MULTIPLE;
+ else
+ flags |= LC_SEL_PIECE;
+
+ if (pPiece->GetGroup() != NULL)
+ {
+ flags |= LC_SEL_GROUP;
+ if (pPiece->IsFocused())
+ flags |= LC_SEL_FOCUSGROUP;
+ }
+
+ if (first)
+ {
+ pGroup = pPiece->GetGroup();
+ first = false;
+ }
+ else
+ {
+ if (pGroup != pPiece->GetGroup())
+ flags |= LC_SEL_CANGROUP;
+ else
+ if (pGroup == NULL)
+ flags |= LC_SEL_CANGROUP;
+ }
+ }
+ else
+ {
+ flags |= LC_SEL_UNSELECTED;
+
+ if (pPiece->IsHidden())
+ flags |= LC_SEL_HIDDEN;
+ }
+ }
+ }
+
+ for (Camera* pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ if (pCamera->IsSelected())
+ flags |= LC_SEL_CAMERA;
+
+ for (Light* pLight = m_pLights; pLight; pLight = pLight->m_pNext)
+ if (pLight->IsSelected())
+ flags |= LC_SEL_LIGHT;
+
+ SystemUpdateSelected(flags);
+}
+
+void Project::CheckAutoSave()
+{
+ m_nSaveTimer += 5;
+ if (m_nAutosave & LC_AUTOSAVE_FLAG)
+ {
+ int nInterval = m_nAutosave & ~LC_AUTOSAVE_FLAG;
+
+ if (m_nSaveTimer >= (m_nAutosave*60))
+ {
+ m_nSaveTimer = 0;
+/*
+ if (m_strTempFile.IsEmpty())
+ {
+ char tmpFile[_MAX_PATH], out[_MAX_PATH];
+ GetTempPath (_MAX_PATH, out);
+ GetTempFileName (out, "~LC", 0, tmpFile);
+ DeleteFile (tmpFile);
+ if (char *ptr = strchr(tmpFile, '.')) *ptr = 0;
+ strcat (tmpFile, ".lcd");
+ m_strTempFile = tmpFile;
+ }
+
+ CFile file (m_strTempFile, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary);
+ m_bUndo = TRUE;
+ file.SeekToBegin();
+ CArchive ar(&file, CArchive::store);
+ Serialize(ar);
+ ar.Close();
+ m_bUndo = FALSE;
+*/ }
+ }
+}
+
+void Project::SetViewSize(int cx, int cy)
+{
+ m_nViewX = cx;
+ m_nViewY = (cy != 0) ? cy : 1; // Avoid divide by zero.
+}
+
+unsigned char Project::GetLastStep()
+{
+ unsigned char last = 1;
+ for (Piece* pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ last = max(last, pPiece->GetStepShow());
+
+ return last;
+}
+
+// Create a series of pictures
+void Project::CreateImages(LC_IMAGE** images, int width, int height, unsigned short from, unsigned short to, bool hilite)
+{
+ int oldx, oldy;
+ unsigned short oldtime;
+ void* render = SystemStartRender(width, height);
+ oldtime = m_bAnimation ? m_nCurFrame : m_nCurStep;
+ oldx = m_nViewX;
+ oldy = m_nViewY;
+ m_nViewX = width;
+ m_nViewY = height;
+
+ if (!hilite)
+ SelectAndFocusNone(false);
+
+ RenderInitialize();
+
+// CCamera* pOld = pDoc->GetActiveCamera();
+// pDoc->m_ViewCameras[pDoc->m_nActiveViewport] = pDoc->GetCamera(CAMERA_MAIN);
+ for (int i = from; i <= to; i++)
+ {
+ if (m_bAnimation)
+ m_nCurFrame = i;
+ else
+ m_nCurStep = i;
+
+ if (hilite)
+ {
+ for (Piece* pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ if ((m_bAnimation && pPiece->GetFrameShow() == i) ||
+ (!m_bAnimation && pPiece->GetStepShow() == i))
+ pPiece->Select();
+ else
+ pPiece->UnSelect();
+ }
+ }
+
+ CalculateStep();
+ Render(true);
+
+ images[i-from] = SystemGetRenderImage(render);
+ }
+// pDoc->m_ViewCameras[pDoc->m_nActiveViewport] = pOld;
+ m_nViewX = oldx;
+ m_nViewY = oldy;
+ if (m_bAnimation)
+ m_nCurFrame = oldtime;
+ else
+ m_nCurStep = (unsigned char)oldtime;
+ CalculateStep();
+ SystemFinishRender(render);
+}
+
+void Project::CreateHTMLPieceList(FILE* f, int nStep, bool bImages, char* ext)
+{
+ Piece* pPiece;
+ int col[LC_MAXCOLORS], ID = 0, c;
+ memset (&col, 0, sizeof (col));
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ if ((pPiece->GetStepShow() == nStep) || (nStep == 0))
+ col[pPiece->GetColor()]++;
+ }
+ fputs("<br><table border=1><tr><td><center>Piece</center></td>\n",f);
+
+ for (c = 0; c < LC_MAXCOLORS; c++)
+ if (col[c])
+ {
+ col[c] = ID;
+ ID++;
+ fprintf(f, "<td><center>%s</center></td>\n", colornames[c]);
+ }
+ ID++;
+ fputs("</tr>\n",f);
+
+ PieceInfo* pInfo;
+ for (int j = 0; j < m_nPieceCount; j++)
+ {
+ bool Add = false;
+ int count[LC_MAXCOLORS];
+ memset (&count, 0, sizeof (count));
+ pInfo = &m_pPieceIdx[j];
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ if ((pPiece->GetPieceInfo() == pInfo) &&
+ ((pPiece->GetStepShow() == nStep) || (nStep == 0)))
+ {
+ count [pPiece->GetColor()]++;
+ Add = true;
+ }
+ }
+
+ if (Add)
+ {
+ if (bImages)
+ fprintf(f, "<tr><td><IMG SRC=\"%s%s\" ALT=\"%s\"></td>\n", pInfo->m_strName, ext, pInfo->m_strDescription);
+ else
+ fprintf(f, "<tr><td>%s</td>\n", pInfo->m_strDescription);
+
+ int curcol = 1;
+ for (c = 0; c < LC_MAXCOLORS; c++)
+ if (count[c])
+ {
+ while (curcol != col[c] + 1)
+ {
+ fputs("<td><center>-</center></td>\n", f);
+ curcol++;
+ }
+
+ fprintf(f, "<td><center>%d</center></td>\n", count[c]);
+ curcol++;
+ }
+
+ while (curcol != ID)
+ {
+ fputs("<td><center>-</center></td>\n", f);
+ curcol++;
+ }
+
+ fputs("</tr>\n", f);
+ }
+ }
+ fputs("</table>\n<br>", f);
+}
+
+// Special notifications.
+void Project::HandleNotify(LC_NOTIFY id, unsigned long param)
+{
+ switch (id)
+ {
+ case LC_COLOR_CHANGED:
+ {
+ m_nCurColor = (unsigned char)param;
+ } break;
+
+ case LC_GROUP_CHANGED:
+ {
+ m_nCurGroup = (unsigned char)param;
+ } break;
+
+ case LC_CAPTURE_LOST:
+ {
+ if (m_nTracking != LC_TRACK_NONE)
+ StopTracking(false);
+ } break;
+
+ // Application is (de)activated
+ case LC_ACTIVATE:
+ {
+ if (param == 0)
+ SystemExportClipboard(m_pClipboard[m_nCurClipboard]);
+ else
+ {
+ free(m_pClipboard[m_nCurClipboard]);
+ m_pClipboard[m_nCurClipboard] = SystemImportClipboard();
+ SystemUpdatePaste(m_pClipboard[m_nCurClipboard] != NULL);
+ }
+ } break;
+
+ case LC_PIECE_MODIFIED:
+ {
+ LC_PIECE_MODIFY* mod = (LC_PIECE_MODIFY*)param;
+ Piece* pPiece = (Piece*)mod->piece;
+
+ float pos[3], rot[4];
+ pPiece->GetPosition(pos);
+ pPiece->GetRotation(rot);
+ Matrix mat(rot, pos);
+ mat.GetEulerAngles(rot);
+
+ if (mod->pos[0] != pos[0] || mod->pos[1] != pos[1] || mod->pos[2] != pos[2])
+ pPiece->ChangeKey(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, mod->pos, PK_POSITION);
+
+ if (mod->rot[0] != rot[0] || mod->rot[1] != rot[1] || mod->rot[2] != rot[2])
+ {
+ mat.FromEuler(mod->rot[0], mod->rot[1], mod->rot[2]);
+ mat.ToAxisAngle(rot);
+ pPiece->ChangeKey(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, rot, PK_ROTATION);
+ }
+
+ if (m_bAnimation)
+ {
+ pPiece->SetFrameShow(mod->from);
+ pPiece->SetFrameHide(mod->to);
+ }
+ else
+ {
+ pPiece->SetStepShow(mod->from);
+ pPiece->SetStepHide(mod->to);
+ }
+
+ if (mod->hidden)
+ pPiece->Hide();
+ else
+ pPiece->UnHide();
+
+ pPiece->SetName(mod->name);
+ pPiece->SetColor(mod->color);
+ pPiece->UpdatePosition(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation);
+ pPiece->CalculateConnections(m_pConnections, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, false, true);
+
+ SetModifiedFlag(true);
+ CheckPoint("Modifying");
+ SystemRedrawView();
+ } break;
+
+ case LC_CAMERA_MODIFIED:
+ {
+ LC_CAMERA_MODIFY* mod = (LC_CAMERA_MODIFY*)param;
+ Camera* pCamera = (Camera*)mod->camera;
+ float tmp[3];
+
+ if (mod->hidden)
+ pCamera->Hide();
+ else
+ pCamera->UnHide();
+
+ pCamera->GetUp(tmp);
+
+ if (tmp[0] != mod->eye[0] || tmp[1] != mod->eye[1] || tmp[2] != mod->eye[2])
+ pCamera->ChangeKey(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, mod->eye, CK_EYE);
+ if (tmp[0] != mod->target[0] || tmp[1] != mod->target[1] || tmp[2] != mod->target[2])
+ pCamera->ChangeKey(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, mod->target, CK_TARGET);
+ if (tmp[0] != mod->up[0] || tmp[1] != mod->up[1] || tmp[2] != mod->up[2])
+ pCamera->ChangeKey(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, mod->up, CK_UP);
+
+ pCamera->m_fovy = mod->fovy;
+ pCamera->m_zNear = mod->znear;
+ pCamera->m_zFar = mod->zfar;
+ pCamera->UpdatePosition(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation);
+ SystemRedrawView();
+ } break;
+
+ case LC_LIGHT_MODIFIED:
+ {
+ } break;
+ }
+}
+
+// Handle (almost) all menu/toolbar commands here.
+void Project::HandleCommand(LC_COMMANDS id, unsigned long nParam)
+{
+ switch (id)
+ {
+ case LC_FILE_NEW:
+ {
+ if (!SaveModified())
+ return; // leave the original one
+
+ OnNewDocument();
+ SystemRedrawView();
+ } break;
+
+ case LC_FILE_OPEN:
+ {
+ char filename[LC_MAXPATH];
+ strcpy(filename, m_strModelsPath);
+
+ if (SystemDoDialog(LC_DLG_FILE_OPEN, filename))
+ {
+ if (!SaveModified())
+ return; // leave the original one
+
+// CWaitCursor wait;
+ bool bWasModified = IsModified();
+ SetModifiedFlag(false); // not dirty for open
+
+ if (!OnOpenDocument(filename))
+ {
+ // check if we corrupted the original document
+ if (!IsModified())
+ SetModifiedFlag(bWasModified);
+ else
+ OnNewDocument();
+
+ return; // open failed
+ }
+ SetPathName(filename, true);
+ }
+ } break;
+
+ case LC_FILE_MERGE:
+ {
+ char filename[LC_MAXPATH];
+ strcpy(filename, m_strModelsPath);
+
+ if (SystemDoDialog(LC_DLG_FILE_MERGE, filename))
+ {
+ File file(false);
+ if (file.Open(filename, "rb"))
+ {
+// CWaitCursor wait;
+ if (file.GetLength() != 0)
+ {
+ FileLoad(&file, false, true);
+ CheckPoint("Merging");
+ }
+ file.Close();
+ }
+ }
+ } break;
+
+ case LC_FILE_SAVE:
+ {
+ DoFileSave();
+ } break;
+
+ case LC_FILE_SAVEAS:
+ {
+ DoSave(NULL, true);
+ } break;
+
+ case LC_FILE_PICTURE:
+ {
+ LC_IMAGEDLG_OPTS opts;
+ opts.from = 1;
+ opts.to = m_bAnimation ? m_nTotalFrames : GetLastStep();
+ opts.multiple = m_bAnimation;
+ opts.imopts.background[0] = (unsigned char)(m_fBackground[0]*255);
+ opts.imopts.background[1] = (unsigned char)(m_fBackground[1]*255);
+ opts.imopts.background[2] = (unsigned char)(m_fBackground[2]*255);
+
+ if (SystemDoDialog(LC_DLG_PICTURE_SAVE, &opts))
+ {
+ if (m_bAnimation)
+ opts.to = min(opts.to, m_nTotalFrames);
+ else
+ opts.to = min(opts.to, 255);
+ opts.from = max(1, opts.from);
+
+ if (opts.multiple)
+ {
+ if (opts.from > opts.to)
+ {
+ unsigned short t = opts.from;
+ opts.from = opts.to;
+ opts.to = t;
+ }
+ }
+ else
+ opts.from = opts.to = m_bAnimation ? m_nCurFrame : m_nCurStep;
+
+ LC_IMAGE** images;
+ images = (LC_IMAGE**)malloc(sizeof(LC_IMAGE*)*(opts.to-opts.from+1));
+ CreateImages(images, opts.width, opts.height, opts.from, opts.to, false);
+
+ char *ptr, ext[4];
+ ptr = strrchr(opts.filename, '.');
+ if (ptr != NULL)
+ {
+ strncpy(ext, ptr+1, 3);
+ strlwr(ext);
+ }
+
+ if (strcmp(ext, "avi") == 0)
+ {
+ SaveVideo(opts.filename, images, opts.to-opts.from+1, m_bAnimation ? m_nFPS : 60.0f/opts.imopts.pause);
+
+ for (int i = 0; i <= opts.to-opts.from; i++)
+ free(images[i]);
+ }
+ else
+ {
+ for (int i = 0; i <= opts.to-opts.from; i++)
+ {
+ char filename[LC_MAXPATH];
+ if (opts.multiple)
+ {
+ char* ext = strrchr(opts.filename, '.');
+ *ext = 0;
+ sprintf(filename, "%s%02d.%s", opts.filename, i, ext+1);
+ }
+ else
+ strcpy(filename, opts.filename);
+
+ SaveImage(filename, images[i], &opts.imopts);
+ free(images[i]);
+ }
+ }
+ free(images);
+ }
+ } break;
+
+ case LC_FILE_3DS:
+ {
+#ifdef _WINDOWS
+ Export3DStudio();
+#endif
+ } break;
+
+ case LC_FILE_HTML:
+ {
+ LC_HTMLDLG_OPTS opts;
+ strcpy(opts.path, m_strPathName);
+ if (strlen(opts.path) > 0)
+ {
+ char* ptr = strrchr(opts.path, '/');
+ if (ptr == NULL)
+ ptr = strrchr(opts.path, '\\');
+ if (ptr)
+ {
+ ptr++;
+ *ptr = 0;
+ }
+ }
+ unsigned long image = SystemGetProfileInt ("Default", "HTML Options", 1|LC_IMAGE_TRANSPARENT);
+ opts.imdlg.imopts.background[0] = (unsigned char)(m_fBackground[0]*255);
+ opts.imdlg.imopts.background[1] = (unsigned char)(m_fBackground[1]*255);
+ opts.imdlg.imopts.background[2] = (unsigned char)(m_fBackground[2]*255);
+ opts.imdlg.from = 1;
+ opts.imdlg.to = 1;
+ opts.imdlg.multiple = false;
+ opts.imdlg.width = SystemGetProfileInt("Default", "HTML Width", 256);
+ opts.imdlg.height = SystemGetProfileInt("Default", "HTML Height", 160);
+ opts.imdlg.imopts.quality = SystemGetProfileInt("Default", "JPEG Quality", 70);
+ opts.imdlg.imopts.interlaced = (image & LC_IMAGE_PROGRESSIVE) != 0;
+ opts.imdlg.imopts.transparent = (image & LC_IMAGE_TRANSPARENT) != 0;
+ opts.imdlg.imopts.truecolor = (image & LC_IMAGE_HIGHCOLOR) != 0;
+ opts.imdlg.imopts.pause = 1;
+ opts.imdlg.imopts.format = (unsigned char)(image & ~(LC_IMAGE_MASK));
+
+ if (SystemDoDialog(LC_DLG_HTML, &opts))
+ {
+ FILE* f;
+ char* ext, fn[LC_MAXPATH];
+ int i;
+ unsigned short last = GetLastStep();
+
+ switch (opts.imdlg.imopts.format)
+ {
+ case 0: ext = ".bmp"; break;
+ case 1: ext = ".gif"; break;
+ case 2: ext = ".jpg"; break;
+ }
+/*
+ // Create destination folder
+ char *MyPath = strdup(dlg.m_strFolder);
+ char *p = MyPath;
+ int psave;
+ while(*p)
+ {
+ while(*p && *p != '\\')
+ ++p;
+
+ psave = *p;
+ if (*p == '\\')
+ *p = 0;
+
+ if (strlen(MyPath) > 3)
+ CreateDirectory(MyPath, NULL);
+
+ *p = psave;
+ if (*p)
+ ++p;
+ }
+ free(MyPath);
+*/
+ if (opts.singlepage)
+ {
+ strcpy(fn, opts.path);
+ strcat(fn, m_strTitle);
+ strcat(fn, ".htm");
+ f = fopen(fn, "wt");
+ fprintf(f, "<HTML>\n<HEAD>\n<TITLE>Instructions for %s</TITLE>\n</HEAD>\n<BR>\n<CENTER>\n", m_strTitle);
+
+ for (i = 1; i <= last; i++)
+ {
+ fprintf(f, "<IMG SRC=\"%s-%02d%s\" ALT=\"Step %02d\" WIDTH=%d HEIGHT=%d><BR><BR>\n",
+ m_strTitle, i, ext, i, opts.imdlg.width, opts.imdlg.height);
+
+ if (opts.liststep)
+ CreateHTMLPieceList(f, i, opts.images, ext);
+ }
+
+ if (opts.listend)
+ CreateHTMLPieceList(f, 0, opts.images, ext);
+
+ fputs("</CENTER>\n<BR><HR><BR><B><I>Created by <A HREF=\"http://www.geocities.com/Colosseum/3479/leocad.htm\">LeoCAD</A></B></I><BR></HTML>\n", f);
+ fclose(f);
+ }
+ else
+ {
+ if (opts.index)
+ {
+ strcpy(fn, opts.path);
+ strcat(fn, m_strTitle);
+ strcat(fn, "-index.htm");
+ f = fopen(fn, "wt");
+
+ fprintf(f, "<HTML>\n<HEAD>\n<TITLE>Instructions for %s</TITLE>\n</HEAD>\n<BR>\n<CENTER>\n", m_strTitle);
+
+ for (i = 1; i <= last; i++)
+ fprintf(f, "<A HREF=\"%s-%02d.htm\">Step %d<BR>\n</A>", m_strTitle, i, i);
+
+ if (opts.listend)
+ fprintf(f, "<A HREF=\"%s-pieces.htm\">Pieces Used</A><BR>\n", m_strTitle);
+
+ fputs("</CENTER>\n<BR><HR><BR><B><I>Created by <A HREF=\"http://www.geocities.com/Colosseum/3479/leocad.htm\">LeoCAD</A></B></I><BR></HTML>\n", f);
+ fclose(f);
+ }
+
+ // Create each step
+ for (i = 1; i <= last; i++)
+ {
+ sprintf(fn, "%s%s-%02d.htm", opts.path, m_strTitle, i);
+ f = fopen(fn, "wt");
+
+ fprintf(f, "<HTML>\n<HEAD>\n<TITLE>%s - Step %02d</TITLE>\n</HEAD>\n<BR>\n<CENTER>\n", m_strTitle, i);
+ fprintf(f, "<IMG SRC=\"%s-%02d%s\" ALT=\"Step %02d\" WIDTH=%d HEIGHT=%d><BR><BR>\n",
+ m_strTitle, i, ext, i, opts.imdlg.width, opts.imdlg.height);
+
+ if (opts.liststep)
+ CreateHTMLPieceList(f, i, opts.images, ext);
+
+ fputs("</CENTER>\n<BR><HR><BR>", f);
+ if (i != 1)
+ fprintf(f, "<A HREF=\"%s-%02d.htm\">Previous</A> ", m_strTitle, i-1);
+
+ if (opts.index)
+ fprintf(f, "<A HREF=\"%s-index.htm\">Index</A> ", m_strTitle);
+
+ if (i != last)
+ fprintf(f, "<A HREF=\"%s-%02d.htm\">Next</A>", m_strTitle, i+1);
+ else
+ if (opts.listend)
+ fprintf(f, "<A HREF=\"%s-pieces.htm\">Pieces Used</A>", m_strTitle);
+
+ fputs("<BR></HTML>\n",f);
+ fclose(f);
+ }
+
+ if (opts.listend)
+ {
+ strcpy(fn, opts.path);
+ strcat(fn, m_strTitle);
+ strcat(fn, "-pieces.htm");
+ f = fopen(fn, "wt");
+ fprintf(f, "<HTML>\n<HEAD>\n<TITLE>Pieces used by %s</TITLE>\n</HEAD>\n<BR>\n<CENTER>\n", m_strTitle);
+
+ CreateHTMLPieceList(f, 0, opts.images, ext);
+
+ fputs("</CENTER>\n<BR><HR><BR>", f);
+ fprintf(f, "<A HREF=\"%s-%02d.htm\">Previous</A> ", m_strTitle, i-1);
+
+ if (opts.index)
+ fprintf(f, "<A HREF=\"%s-index.htm\">Index</A> ", m_strTitle);
+
+ fputs("<BR></HTML>\n",f);
+ fclose(f);
+ }
+ }
+
+ // Save step pictures
+ LC_IMAGE** images;
+ images = (LC_IMAGE**)malloc(sizeof(LC_IMAGE*)*last);
+ CreateImages(images, opts.imdlg.width, opts.imdlg.height, 1, last, opts.hilite);
+
+ for (i = 0; i < last; i++)
+ {
+ sprintf(fn, "%s%s-%02d%s", opts.path, m_strTitle, i+1, ext);
+ SaveImage(fn, images[i], &opts.imdlg.imopts);
+ free(images[i]);
+ }
+ free(images);
+
+ if (opts.images)
+ {
+ int cx = 120, cy = 100;
+ void* render = SystemStartRender(cx, cy);
+
+ float aspect = (float)cx/(float)cy;
+ glViewport(0, 0, cx, cy);
+
+ Piece *p1, *p2;
+ PieceInfo* pInfo;
+ for (p1 = m_pPieces; p1; p1 = p1->m_pNext)
+ {
+ bool bSkip = false;
+ pInfo = p1->GetPieceInfo();
+
+ for (p2 = m_pPieces; p2; p2 = p2->m_pNext)
+ {
+ if (p2 == p1)
+ break;
+
+ if (p2->GetPieceInfo() == pInfo)
+ {
+ bSkip = true;
+ break;
+ }
+ }
+
+ if (bSkip)
+ continue;
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective(30.0f, aspect, 1.0f, 50.0f);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glDepthFunc(GL_LEQUAL);
+ glClearColor(1,1,1,1);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
+ glEnable(GL_COLOR_MATERIAL);
+ glDisable (GL_DITHER);
+ glShadeModel(GL_FLAT);
+ pInfo->ZoomExtents();
+
+ float pos[4] = { 0, 0, 10, 0 };
+ glLightfv(GL_LIGHT0, GL_POSITION, pos);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_DEPTH_TEST);
+ pInfo->RenderPiece(m_nCurColor);
+ glFinish();
+
+ LC_IMAGE* image = SystemGetRenderImage(render);
+ sprintf(fn, "%s%s%s", opts.path, pInfo->m_strName, ext);
+ SaveImage(fn, image, &opts.imdlg.imopts);
+ free(image);
+ }
+ SystemFinishRender(render);
+ }
+ }
+ } break;
+
+ // Export to POV-Ray, swap X & Y from our cs to work with LGEO.
+ case LC_FILE_POVRAY:
+ {
+ LC_POVRAYDLG_OPTS opts;
+ if (!SystemDoDialog(LC_DLG_POVRAY, &opts))
+ break;
+
+// CWaitCursor wc;
+ char fn[LC_MAXPATH], tmp[10], *ptr;
+ unsigned long u;
+ PieceInfo* pInfo;
+ Piece* pPiece;
+ FILE* f;
+ char* conv = (char*)malloc(9*m_nPieceCount);
+ memset(conv, 0, 9*m_nPieceCount);
+
+ // read LGEO conversion table
+ if (strlen(opts.libpath))
+ {
+ strcpy(fn, opts.libpath);
+ strcat(fn, "l2p_elmt.tab");
+ f = fopen(fn, "rb");
+
+ if (f == NULL)
+ {
+// AfxMessageBox(IDS_OPENFILE_ERROR, MB_OK|MB_ICONSTOP);
+ return;
+ }
+
+ unsigned char bt[4];
+ while (fread(&bt, 4, 1, f))
+ {
+ u = (((unsigned char)(bt[3])|((unsigned short)(bt[2]) << 8))|(((unsigned long)(bt[1])) << 16)) + bt[0] * 16581375;
+ sprintf(tmp, "%d", u);
+ pInfo = FindPieceInfo(tmp);
+
+ fread(&tmp, 9, 1, f);
+ if (tmp[8] != 0)
+ fread(&tmp[9], 1, 1, f);
+
+ if (pInfo != NULL)
+ {
+ int idx = (((char*)pInfo - (char*)m_pPieceIdx)/sizeof(PieceInfo));
+ memcpy(&conv[idx*9], &tmp[1], 9);
+ }
+ }
+ fclose(f);
+
+ strcpy(fn, opts.libpath);
+ strcat(fn, "l2p_ptrn.tab");
+ f = fopen(fn, "rb");
+
+ if (f == NULL)
+ {
+// AfxMessageBox(IDS_OPENFILE_ERROR, MB_OK|MB_ICONSTOP);
+ free(conv);
+ return;
+ }
+
+ u = 0;
+ do
+ {
+ if ((tmp[u] == 0) && (u != 0))
+ {
+ u = 0;
+ pInfo = FindPieceInfo(tmp);
+ fread(&tmp, 8, 1, f);
+
+ if (pInfo != NULL)
+ {
+ int idx = (((char*)pInfo - (char*)m_pPieceIdx)/sizeof(PieceInfo));
+ memcpy(&conv[idx*9], tmp, 9);
+ }
+ }
+ else
+ u++;
+ }
+ while (fread(&tmp[u], 1, 1, f));
+ fclose(f);
+ }
+
+ strcpy(fn, opts.outpath);
+ if (ptr = strrchr (fn, '.'))
+ *ptr = 0;
+ strcat (fn, ".inc");
+ f = fopen(fn, "wt");
+ fputs("// Stuff that doesn't need to be changed\n\n", f);
+
+ if (strlen(opts.libpath))
+ fputs("#include \"lg_color.inc\"\n#include \"lg_defs.inc\"\n\n", f);
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ Piece* pNext;
+
+ for (pNext = m_pPieces; pNext; pNext = pNext->m_pNext)
+ {
+ pInfo = pNext->GetPieceInfo();
+
+ if (pNext == pPiece)
+ {
+ int idx = (((char*)pInfo - (char*)m_pPieceIdx)/sizeof(PieceInfo));
+ char pat[] = "patterns/";
+ if (conv[idx*9+1] != 'p')
+ strcpy(pat, "");
+
+ if (conv[idx*9] != 0)
+ fprintf(f, "#include \"%s%s.inc\"\n", pat, &conv[idx*9]);
+ break;
+ }
+
+ if (pInfo == pPiece->GetPieceInfo())
+ break;
+ }
+ }
+
+ const char* lg_colors[28] = { "red", "Orange", "green", "mint", "blue", "LightBlue", "yellow",
+ "white", "dark_grey", "black", "brown", "pink", "purple", "gold_chrome", "clear_red",
+ "clear_neon_orange", "clear_green", "clear_neon_yellow", "clear_blue", "clear_cyan",
+ "clear_yellow", "clear", "grey", "tan", "LightBrown", "rose", "Turquoise", "chrome" };
+
+ const float mycol[4][4] = { { 1.0f, 0.5f, 0.2f, 1 }, { 0.2f, 0.4f, 0.9f, 5 },
+ { 0.6f, 0.4f, 0.4f, 24 }, { 0.1f, 0.7f, 0.8f, 26 }};
+
+ for (u = 0; u < 4; u++)
+ fprintf(f, "\n#declare lg_%s = texture {\n pigment { rgb <%.2f, %.2f, %.2f> }\n finish {\n ambient 0.1\n phong 0.3\n phong_size 20\n }\n}\n",
+ altcolornames[(int)mycol[u][3]], mycol[u][0], mycol[u][1], mycol[u][2]);
+
+ // if not in lgeo, create it
+ fputs("\n// The next objects (if any) were generated by LeoCAD.\n\n", f);
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ pInfo = pPiece->GetPieceInfo();
+ int idx = (((char*)pInfo - (char*)m_pPieceIdx)/sizeof(PieceInfo));
+ if (conv[idx*9] != 0)
+ continue;
+
+ for (Piece* pNext = m_pPieces; pNext; pNext = pNext->m_pNext)
+ {
+ if (pNext == pPiece)
+ {
+ char name[20];
+ strcpy(name, pInfo->m_strName);
+ while (ptr = strchr(name, '-'))
+ *ptr = '_';
+ fprintf(f, "#declare lc_%s = union {\n", name);
+
+ unsigned short g;
+ for (g = 0; g < pInfo->m_nGroupCount; g++)
+ if (pInfo->m_nFlags & LC_PIECE_LONGDATA)
+ {
+ unsigned long* info = (unsigned long*)pInfo->m_pGroups[g].drawinfo;
+ unsigned long count, curcolor, colors = *info;
+ info++;
+
+ while (colors--)
+ {
+ curcolor = *info;
+ info++;
+
+ // skip if color only have lines
+ if ((*info == 0) && (info[1] == 0))
+ {
+ info += 2;
+ info += *info + 1;
+ continue;
+ }
+
+ fputs(" mesh {\n", f);
+
+ for (count = *info, info++; count; count -= 4)
+ {
+ fprintf(f, " triangle { <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f> }\n",
+ -pInfo->m_fVertexArray[info[0]*3+1], -pInfo->m_fVertexArray[info[0]*3], pInfo->m_fVertexArray[info[0]*3+2],
+ -pInfo->m_fVertexArray[info[1]*3+1], -pInfo->m_fVertexArray[info[1]*3], pInfo->m_fVertexArray[info[1]*3+2],
+ -pInfo->m_fVertexArray[info[2]*3+1], -pInfo->m_fVertexArray[info[2]*3], pInfo->m_fVertexArray[info[2]*3+2]);
+ fprintf(f, " triangle { <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f> }\n",
+ -pInfo->m_fVertexArray[info[2]*3+1], -pInfo->m_fVertexArray[info[2]*3], pInfo->m_fVertexArray[info[2]*3+2],
+ -pInfo->m_fVertexArray[info[3]*3+1], -pInfo->m_fVertexArray[info[3]*3], pInfo->m_fVertexArray[info[3]*3+2],
+ -pInfo->m_fVertexArray[info[0]*3+1], -pInfo->m_fVertexArray[info[0]*3], pInfo->m_fVertexArray[info[0]*3+2]);
+ info += 4;
+ }
+
+ for (count = *info, info++; count; count -= 3)
+ {
+ fprintf(f, " triangle { <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f> }\n",
+ -pInfo->m_fVertexArray[info[0]*3+1], -pInfo->m_fVertexArray[info[0]*3], pInfo->m_fVertexArray[info[0]*3+2],
+ -pInfo->m_fVertexArray[info[1]*3+1], -pInfo->m_fVertexArray[info[1]*3], pInfo->m_fVertexArray[info[1]*3+2],
+ -pInfo->m_fVertexArray[info[2]*3+1], -pInfo->m_fVertexArray[info[2]*3], pInfo->m_fVertexArray[info[2]*3+2]);
+ info += 3;
+ }
+ info += *info + 1;
+
+ if (curcolor != LC_COL_DEFAULT && curcolor != LC_COL_EDGES)
+ fprintf (f, " texture { lg_%s }\n", lg_colors[curcolor]);
+ fputs(" }\n", f);
+ }
+ }
+ else
+ {
+ unsigned short* info = (unsigned short*)pInfo->m_pGroups[g].drawinfo;
+ unsigned short count, curcolor, colors = *info;
+ info++;
+
+ while (colors--)
+ {
+ curcolor = *info;
+ info++;
+
+ // skip if color only have lines
+ if ((*info == 0) && (info[1] == 0))
+ {
+ info += 2;
+ info += *info + 1;
+ continue;
+ }
+
+ fputs(" mesh {\n", f);
+
+ for (count = *info, info++; count; count -= 4)
+ {
+ fprintf(f, " triangle { <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f> }\n",
+ -pInfo->m_fVertexArray[info[0]*3+1], -pInfo->m_fVertexArray[info[0]*3], pInfo->m_fVertexArray[info[0]*3+2],
+ -pInfo->m_fVertexArray[info[1]*3+1], -pInfo->m_fVertexArray[info[1]*3], pInfo->m_fVertexArray[info[1]*3+2],
+ -pInfo->m_fVertexArray[info[2]*3+1], -pInfo->m_fVertexArray[info[2]*3], pInfo->m_fVertexArray[info[2]*3+2]);
+ fprintf(f, " triangle { <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f> }\n",
+ -pInfo->m_fVertexArray[info[2]*3+1], -pInfo->m_fVertexArray[info[2]*3], pInfo->m_fVertexArray[info[2]*3+2],
+ -pInfo->m_fVertexArray[info[3]*3+1], -pInfo->m_fVertexArray[info[3]*3], pInfo->m_fVertexArray[info[3]*3+2],
+ -pInfo->m_fVertexArray[info[0]*3+1], -pInfo->m_fVertexArray[info[0]*3], pInfo->m_fVertexArray[info[0]*3+2]);
+ info += 4;
+ }
+
+ for (count = *info, info++; count; count -= 3)
+ {
+ fprintf(f, " triangle { <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f> }\n",
+ -pInfo->m_fVertexArray[info[0]*3+1], -pInfo->m_fVertexArray[info[0]*3], pInfo->m_fVertexArray[info[0]*3+2],
+ -pInfo->m_fVertexArray[info[1]*3+1], -pInfo->m_fVertexArray[info[1]*3], pInfo->m_fVertexArray[info[1]*3+2],
+ -pInfo->m_fVertexArray[info[2]*3+1], -pInfo->m_fVertexArray[info[2]*3], pInfo->m_fVertexArray[info[2]*3+2]);
+ info += 3;
+ }
+ info += *info + 1;
+
+ if (curcolor != LC_COL_DEFAULT && curcolor != LC_COL_EDGES)
+ fprintf (f, " texture { lg_%s }\n", lg_colors[curcolor]);
+ fputs(" }\n", f);
+ }
+ }
+
+ fputs("}\n\n", f);
+ break;
+ }
+
+ if (pNext->GetPieceInfo() == pInfo)
+ break;
+ }
+ }
+
+ fclose(f);
+ if (ptr = strrchr (fn, '.'))
+ *ptr = 0;
+ strcat (fn, ".pov");
+ f = fopen(fn, "wt");
+
+ if (ptr = strrchr (fn, '.'))
+ *ptr = 0;
+ ptr = strrchr (fn, '\\');
+ if (!ptr)
+ ptr = strrchr (fn, '/');
+ if (ptr)
+ ptr++;
+ else
+ ptr = fn;
+
+ fprintf(f, "// File created by LeoCAD\n//\n\n#include \"%s.inc\"\n", ptr);
+ float eye[3], target[3], up[3];
+ m_pViewCameras[m_nActiveViewport]->GetEye(eye);
+ m_pViewCameras[m_nActiveViewport]->GetTarget(target);
+ m_pViewCameras[m_nActiveViewport]->GetUp(up);
+
+ fprintf(f, "\ncamera {\n sky<%1g,%1g,%1g>\n location <%1g, %1g, %1g>\n look_at <%1g, %1g, %1g>\n}\n\n",
+ up[0], up[1], up[2], eye[1], eye[0], eye[2], target[1], target[0], target[2]);
+ fprintf(f, "background { color rgb <%1g, %1g, %1g> }\n\nlight_source { <0, 0, 20> White shadowless }\n\n",
+ m_fBackground[0], m_fBackground[1], m_fBackground[2]);
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ float fl[12], pos[3], rot[4];
+ char name[20];
+ int idx = (((char*)pPiece->GetPieceInfo() - (char*)m_pPieceIdx)/sizeof(PieceInfo));
+
+ if (conv[idx*9] == 0)
+ {
+ char* ptr;
+ sprintf(name, "lc_%s", pPiece->GetPieceInfo()->m_strName);
+ while (ptr = strchr(name, '-'))
+ *ptr = '_';
+ }
+ else
+ {
+ strcpy (name, &conv[idx*9]);
+ if (pPiece->IsTransparent())
+ strcat(name, "_clear");
+ }
+
+ pPiece->GetPosition(pos);
+ pPiece->GetRotation(rot);
+ Matrix mat(rot, pos);
+ mat.ConvertToLDraw(fl);
+
+ fprintf(f, "object {\n %s\n texture { lg_%s } matrix <%.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f>\n}\n",
+ name, lg_colors[pPiece->GetColor()], -fl[11], -fl[5], fl[8], -fl[9], -fl[3], fl[6],
+ -fl[10], -fl[4], fl[7], pos[1], pos[0], pos[2]);
+ }
+ fclose(f);
+ free(conv);
+
+ if (opts.render)
+ {
+#ifdef _WINDOWS
+ // TODO: Linux support
+ char buf[600];
+ char tmp[LC_MAXPATH], out[LC_MAXPATH];
+ sprintf(out, "%s.pov", fn);
+ GetShortPathName(out, out, LC_MAXPATH);
+ GetShortPathName(opts.libpath, tmp, LC_MAXPATH);
+ sprintf (buf, "+L%s +I%s", tmp, out);
+ ptr = strrchr (out, '\\');
+ if (ptr)
+ *(ptr+1) = 0;
+ ShellExecute(::GetDesktopWindow(), "open", opts.povpath, buf, out, SW_SHOWNORMAL);
+#endif
+ }
+ } break;
+
+ case LC_FILE_WAVEFRONT:
+ {
+ char filename[LC_MAXPATH];
+ if (!SystemDoDialog(LC_DLG_WAVEFRONT, filename))
+ break;
+
+ char buf[LC_MAXPATH], *ptr;
+ FILE* stream = fopen(filename, "wt");
+ unsigned long vert = 1, i;
+ Piece* pPiece;
+
+ strcpy(buf, m_strPathName);
+ ptr = strrchr(buf, '\\');
+ if (ptr)
+ ptr++;
+ else
+ {
+ ptr = strrchr(buf, '/');
+ if (ptr)
+ ptr++;
+ else
+ ptr = buf;
+ }
+ fputs("# Model exported from LeoCAD\n", stream);
+ if (strlen(buf) != 0)
+ fprintf(stream,"# Original name: %s\n", ptr);
+ if (strlen(m_strAuthor))
+ fprintf(stream, "# Author: %s\n", m_strAuthor);
+
+ strcpy(buf, filename);
+ ptr = strrchr(buf, '.');
+ if (ptr)
+ *ptr = 0;
+
+ strcat(buf, ".mtl");
+ ptr = strrchr(buf, '\\');
+ if (ptr)
+ ptr++;
+ else
+ {
+ ptr = strrchr(buf, '/');
+ if (ptr)
+ ptr++;
+ else
+ ptr = buf;
+ }
+
+ fprintf(stream, "#\n\nmtllib %s\n\n", ptr);
+
+ FILE* mat = fopen(buf, "wt");
+ fputs("# Colors used by LeoCAD\n# You need to add transparency values\n#\n\n", mat);
+ for (i = 0; i < LC_MAXCOLORS; i++)
+ fprintf(mat, "newmtl %s\nKd %.2f %.2f %.2f\n\n", altcolornames[i], FlatColorArray[i][0], FlatColorArray[i][1], FlatColorArray[i][2]);
+ fclose(mat);
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ float pos[3], rot[4], tmp[3];
+ pPiece->GetPosition(pos);
+ pPiece->GetRotation(rot);
+ Matrix mat(rot, pos);
+ PieceInfo* pInfo = pPiece->GetPieceInfo();
+
+ for (i = 0; i < pInfo->m_nVertexCount*3; i += 3)
+ {
+ mat.TransformPoint(tmp, &pInfo->m_fVertexArray[i]);
+ fprintf(stream, "v %.2f %.2f %.2f\n", tmp[0], tmp[1], tmp[2]);
+ }
+ fputs("#\n\n", stream);
+ }
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ strcpy(buf, pPiece->GetName());
+ for (i = 0; i < strlen(buf); i++)
+ if ((buf[i] == '#') || (buf[i] == ' '))
+ buf[i] = '_';
+
+ fprintf(stream, "g %s\n", buf);
+ pPiece->GetPieceInfo()->WriteWavefront(stream, pPiece->GetColor(), &vert);
+ }
+
+ fclose(stream);
+ } break;
+
+ case LC_FILE_PROPERTIES:
+ {
+ LC_PROPERTIESDLG_OPTS opts;
+
+ opts.strTitle = m_strTitle;
+ strcpy(opts.strAuthor, m_strAuthor);
+ strcpy(opts.strDescription, m_strDescription);
+ strcpy(opts.strComments, m_strComments);
+ opts.strFilename = m_strPathName;
+
+ opts.lines = m_nPieceCount;
+ opts.count = (unsigned short*)malloc(m_nPieceCount*LC_MAXCOLORS*sizeof(unsigned short));
+ memset (opts.count, 0, m_nPieceCount*LC_MAXCOLORS*sizeof(unsigned short));
+ opts.names = (char**)malloc(m_nPieceCount*sizeof(char*));
+ for (int i = 0; i < m_nPieceCount; i++)
+ opts.names[i] = m_pPieceIdx[i].m_strDescription;
+
+ for (Piece* pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ int idx = (((char*)pPiece->GetPieceInfo() - (char*)m_pPieceIdx)/sizeof(PieceInfo));
+ opts.count[idx*LC_MAXCOLORS+pPiece->GetColor()]++;
+ }
+
+ if (SystemDoDialog(LC_DLG_PROPERTIES, &opts))
+ {
+ if (strcmp(m_strAuthor, opts.strAuthor) ||
+ strcmp(m_strDescription, opts.strDescription) ||
+ strcmp(m_strComments, opts.strComments))
+ {
+ strcpy(m_strAuthor, opts.strAuthor);
+ strcpy(m_strDescription, opts.strDescription);
+ strcpy(m_strComments, opts.strComments);
+ SetModifiedFlag(true);
+ }
+ }
+
+ free(opts.count);
+ free(opts.names);
+ } break;
+
+ case LC_FILE_TERRAIN:
+ {
+ Terrain* temp = new Terrain();
+ *temp = *m_pTerrain;
+
+ if (SystemDoDialog(LC_DLG_TERRAIN, temp))
+ {
+ *m_pTerrain = *temp;
+ m_pTerrain->LoadTexture((m_nDetail & LC_DET_LINEAR) != 0);
+ }
+ delete temp;
+ } break;
+
+ case LC_FILE_LIBRARY:
+ {
+ File file(true);
+ FileSave(&file, true);
+
+ if (SystemDoDialog(LC_DLG_LIBRARY, NULL))
+ {
+/*
+ for (i = 0; i < pDoc->m_PartsIdx.GetSize(); i++)
+ pDoc->m_PartsIdx[i].DeleteInformation();
+
+ pDoc->LoadPieceLibrary();
+ pDoc->m_bUndo = TRUE;
+ PartFile.SeekToBegin();
+ CArchive ar(&PartFile, CArchive::load);
+ pDoc->Serialize(ar);
+ ar.Close();
+ pDoc->m_bUndo = FALSE;
+ pDoc->RebuildDisplayLists(FALSE);
+ pDoc->UpdateAllViews(NULL);
+ m_wndPiecesBar.m_wndPiecesList.UpdateList();
+ }
+*/
+ }
+ } break;
+
+ case LC_FILE_RECENT:
+ {
+ if (!SaveModified())
+ break; // leave the original one
+
+// CWaitCursor wait;
+ bool bWasModified = IsModified();
+ SetModifiedFlag(false); // not dirty for open
+
+ if (!OnOpenDocument(m_strRecentFiles[nParam]))
+ {
+ // check if we corrupted the original document
+ if (!IsModified())
+ SetModifiedFlag(bWasModified);
+ else
+ OnNewDocument();
+
+ for (int i = nParam; i < 3; i++)
+ strcpy(m_strRecentFiles[i], m_strRecentFiles[i+1]);
+ memset(m_strRecentFiles[3], 0, LC_MAXPATH);
+ SystemUpdateRecentMenu(m_strRecentFiles);
+
+ return; // open failed
+ }
+ SetPathName(m_strRecentFiles[nParam], false);
+ } break;
+
+ case LC_EDIT_UNDO:
+ case LC_EDIT_REDO:
+ {
+ UNDOINFO *pUndo, *pTmp;
+ int i;
+
+ if (id == LC_EDIT_UNDO)
+ {
+ if ((m_pUndoList != NULL) && (m_pUndoList->pNext != NULL))
+ {
+ // Remove the first item from the undo list.
+ pUndo = m_pUndoList;
+ m_pUndoList = pUndo->pNext;
+
+ // Check if we need to delete the last redo info.
+ for (pTmp = m_pRedoList, i = 0; pTmp; pTmp = pTmp->pNext, i++)
+ if ((i == 29) && (pTmp->pNext != NULL))
+ {
+ delete pTmp->pNext;
+ pTmp->pNext = NULL;
+ }
+
+ pUndo->pNext = m_pRedoList;
+ m_pRedoList = pUndo;
+
+ pUndo = m_pUndoList;
+ DeleteContents(true);
+ FileLoad(&pUndo->file, true, false);
+ }
+
+ if (m_bUndoOriginal && (m_pUndoList != NULL) && (m_pUndoList->pNext == NULL))
+ SetModifiedFlag(false);
+ }
+ else
+ {
+ if (m_pRedoList != NULL)
+ {
+ // Remove the first element from the redo list.
+ pUndo = m_pRedoList;
+ m_pRedoList = pUndo->pNext;
+
+ // Check if we can delete the last undo info.
+ for (pTmp = m_pUndoList, i = 0; pTmp; pTmp = pTmp->pNext, i++)
+ if ((i == 30) && (pTmp->pNext != NULL))
+ {
+ delete pTmp->pNext;
+ pTmp->pNext = NULL;
+ }
+
+ // Add info to the start of the undo list.
+ pUndo->pNext = m_pUndoList;
+ m_pUndoList = pUndo;
+
+ // Load state.
+ DeleteContents(true);
+ FileLoad(&pUndo->file, true, false);
+ }
+ }
+
+ SystemUpdateUndoRedo(m_pUndoList->pNext ? m_pUndoList->strText : NULL, m_pRedoList ? m_pRedoList->strText : NULL);
+ } break;
+
+ case LC_EDIT_CUT:
+ case LC_EDIT_COPY:
+ {
+ if (IsDrawing())
+ return;
+
+ if (m_pClipboard[m_nCurClipboard] != NULL)
+ delete m_pClipboard[m_nCurClipboard];
+ m_pClipboard[m_nCurClipboard] = new File(true);
+
+ int i = 0;
+ Piece* pPiece;
+ Camera* pCamera;
+ Group* pGroup;
+// Light* pLight;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ i++;
+ m_pClipboard[m_nCurClipboard]->Write(&i, sizeof(i));
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ pPiece->FileSave(m_pClipboard[m_nCurClipboard], m_pGroups);
+
+ for (i = 0, pGroup = m_pGroups; pGroup; pGroup = pGroup->m_pNext)
+ i++;
+ m_pClipboard[m_nCurClipboard]->Write(&i, sizeof(i));
+
+ for (pGroup = m_pGroups; pGroup; pGroup = pGroup->m_pNext)
+ pGroup->FileSave(m_pClipboard[m_nCurClipboard], m_pGroups);
+
+ for (i = 0, pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ if (pCamera->IsSelected())
+ i++;
+ m_pClipboard[m_nCurClipboard]->Write(&i, sizeof(i));
+
+ for (pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ if (pCamera->IsSelected())
+ pCamera->FileSave(m_pClipboard[m_nCurClipboard]);
+/*
+ for (i = 0, pLight = m_pLights; pLight; pLight = pLight->m_pNext)
+ if (pLight->IsSelected())
+ i++;
+ m_pClipboard[m_nCurClipboard]->Write(&i, sizeof(i));
+
+ for (pLight = m_pLights; pLight; pLight = pLight->m_pNext)
+ if (pLight->IsSelected())
+ pLight->FileSave(m_pClipboard[m_nCurClipboard]);
+*/
+ if (id == LC_EDIT_CUT)
+ {
+ RemoveSelectedObjects();
+ SystemUpdateFocus(NULL, LC_UPDATE_OBJECT);
+ UpdateSelection();
+ SystemRedrawView();
+ SetModifiedFlag(true);
+ CheckPoint("Cutting");
+ }
+ SystemExportClipboard(m_pClipboard[m_nCurClipboard]);
+ SystemUpdatePaste(true);
+ } break;
+
+ case LC_EDIT_PASTE:
+ {
+ if (IsDrawing())
+ return;
+
+ int i, j;
+ Piece* pPasted = NULL;
+ File* file = m_pClipboard[m_nCurClipboard];
+ if (file == NULL)
+ break;
+ file->Seek(0, SEEK_SET);
+ SelectAndFocusNone(false);
+
+ file->Read(&i, sizeof(i));
+ while (i--)
+ {
+ char name[9];
+ Piece* pPiece = new Piece(NULL);
+ pPiece->FileLoad(file, name);
+ PieceInfo* pInfo = FindPieceInfo(name);
+ if (pInfo)
+ {
+ pPiece->SetPieceInfo(pInfo);
+ pPiece->m_pNext = pPasted;
+ pPasted = pPiece;
+ }
+ else
+ delete pPiece;
+ }
+
+ file->Read(&i, sizeof(i));
+ Piece* pPiece;
+ Group** groups = (Group**)malloc(i*sizeof(Group**));
+ for (j = 0; j < i; j++)
+ {
+ groups[j] = new Group();
+ groups[j]->FileLoad(file);
+ }
+
+ while (pPasted)
+ {
+ pPiece = pPasted;
+ pPasted = pPasted->m_pNext;
+ pPiece->CreateName(m_pPieces);
+ pPiece->SetFrameShow(m_nCurFrame);
+ pPiece->SetStepShow(m_nCurStep);
+ AddPiece(pPiece);
+ pPiece->Select();
+
+ j = (int)pPiece->GetGroup();
+ if (j != -1)
+ pPiece->SetGroup(groups[j]);
+ else
+ pPiece->UnGroup(NULL);
+ }
+
+ for (j = 0; j < i; j++)
+ {
+ int g = (int)groups[j]->m_pGroup;
+ groups[j]->m_pGroup = (g != -1) ? groups[g] : NULL;
+ }
+
+ for (j = 0; j < i; j++)
+ {
+ Group* pGroup;
+ bool add = false;
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ for (pGroup = pPiece->GetGroup(); pGroup; pGroup = pGroup->m_pGroup)
+ if (pGroup == groups[j])
+ {
+ add = true;
+ break;
+ }
+
+ if (add)
+ break;
+ }
+
+ if (add)
+ {
+ int a, max = 0;
+
+ for (pGroup = m_pGroups; pGroup; pGroup = pGroup->m_pNext)
+ if (strncmp("Pasted Group #", pGroup->m_strName, 14) == 0)
+ if (sscanf(pGroup->m_strName + 14, "%d", &a) == 1)
+ if (a > max)
+ max = a;
+
+ sprintf(groups[j]->m_strName, "Pasted Group #%0.2d", max+1);
+ groups[j]->m_pNext = m_pGroups;
+ m_pGroups = groups[j];
+ }
+ else
+ delete groups[j];
+ }
+
+ free(groups);
+
+ Camera* pCamera = m_pCameras;
+ while (pCamera->m_pNext)
+ pCamera = pCamera->m_pNext;
+ file->Read(&i, sizeof(i));
+
+ while (i--)
+ {
+ pCamera = new Camera(8, pCamera);
+ pCamera->FileLoad(file);
+ pCamera->Select();
+ }
+
+ // TODO: lights
+ CalculateStep();
+ SetModifiedFlag(true);
+ CheckPoint("Pasting");
+ SystemUpdateFocus(NULL, LC_UPDATE_OBJECT);
+ UpdateSelection();
+ SystemRedrawView();
+ } break;
+
+ case LC_EDIT_SELECT_ALL:
+ {
+ Piece* pPiece;
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation))
+ pPiece->Select();
+
+// pFrame->UpdateInfo();
+ UpdateSelection();
+ SystemRedrawView();
+ } break;
+
+ case LC_EDIT_SELECT_NONE:
+ {
+ SelectAndFocusNone(false);
+ SystemUpdateFocus(NULL, LC_UPDATE_OBJECT);
+ UpdateSelection();
+ SystemRedrawView();
+ } break;
+
+ case LC_EDIT_SELECT_INVERT:
+ {
+ Piece* pPiece;
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation))
+ {
+ if (pPiece->IsSelected())
+ pPiece->UnSelect();
+ else
+ pPiece->Select();
+ }
+
+ SystemUpdateFocus(NULL, LC_UPDATE_OBJECT);
+ UpdateSelection();
+ SystemRedrawView();
+ } break;
+
+ case LC_EDIT_SELECT_BYNAME:
+ {
+ Piece* pPiece;
+ Camera* pCamera;
+ Light* pLight;
+ Group* pGroup;
+ int i = 0;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation))
+ i++;
+
+ for (pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ if (pCamera != m_pViewCameras[m_nActiveViewport])
+ if (pCamera->IsVisible())
+ i++;
+
+ for (pLight = m_pLights; pLight; pLight = pLight->m_pNext)
+ if (pLight->IsVisible())
+ i++;
+
+ // TODO: add only groups with visible pieces
+ for (pGroup = m_pGroups; pGroup; pGroup = pGroup->m_pNext)
+ i++;
+
+ if (i == 0)
+ {
+ // TODO: say 'Nothing to select'
+ break;
+ }
+
+ LC_SEL_DATA* opts = (LC_SEL_DATA*)malloc((i+1)*sizeof(LC_SEL_DATA));
+ i = 0;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation))
+ {
+ opts[i].name = pPiece->GetName();
+ opts[i].type = LC_SELDLG_PIECE;
+ opts[i].selected = pPiece->IsSelected();
+ opts[i].pointer = pPiece;
+ i++;
+ }
+
+ for (pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ if (pCamera != m_pViewCameras[m_nActiveViewport])
+ if (pCamera->IsVisible())
+ {
+ opts[i].name = pCamera->GetName();
+ opts[i].type = LC_SELDLG_CAMERA;
+ opts[i].selected = pCamera->IsSelected();
+ opts[i].pointer = pCamera;
+ i++;
+ }
+
+ for (pLight = m_pLights; pLight; pLight = pLight->m_pNext)
+ if (pLight->IsVisible())
+ {
+ opts[i].name = pLight->GetName();
+ opts[i].type = LC_SELDLG_LIGHT;
+ opts[i].selected = pLight->IsSelected();
+ opts[i].pointer = pLight;
+ i++;
+ }
+
+ // TODO: add only groups with visible pieces
+ for (pGroup = m_pGroups; pGroup; pGroup = pGroup->m_pNext)
+ {
+ opts[i].name = pGroup->m_strName;
+ opts[i].type = LC_SELDLG_GROUP;
+ // TODO: check if selected
+ opts[i].selected = false;
+ opts[i].pointer = pGroup;
+ i++;
+ }
+ opts[i].pointer = NULL;
+
+ if (SystemDoDialog(LC_DLG_SELECTBYNAME, opts))
+ {
+ SelectAndFocusNone(false);
+
+ for (i = 0; opts[i].pointer != NULL; i++)
+ {
+ if (!opts[i].selected)
+ continue;
+
+ switch (opts[i].type)
+ {
+ case LC_SELDLG_PIECE:
+ {
+ ((Piece*)opts[i].pointer)->Select();
+ } break;
+
+ case LC_SELDLG_CAMERA:
+ {
+ ((Camera*)opts[i].pointer)->Select();
+ } break;
+
+ case LC_SELDLG_LIGHT:
+ {
+ ((Light*)opts[i].pointer)->Select();
+ } break;
+
+ case LC_SELDLG_GROUP:
+ {
+ pGroup = (Group*)opts[i].pointer;
+ pGroup = pGroup->GetTopGroup();
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->GetTopGroup() == pGroup)
+ pPiece->Select();
+ } break;
+ }
+ }
+
+ UpdateSelection();
+ SystemRedrawView();
+// pFrame->UpdateInfo();
+ }
+
+ free(opts);
+ } break;
+
+ case LC_PIECE_INSERT:
+ {
+ if (m_pCurPiece == NULL)
+ break;
+ Piece* pLast = NULL;
+ Piece* pPiece = new Piece(m_pCurPiece);
+
+ for (pLast = m_pPieces; pLast; pLast = pLast->m_pNext)
+ if ((pLast->IsFocused()) || (pLast->m_pNext == NULL))
+ break;
+
+ if (pLast != NULL)
+ {
+ float pos[3], rot[4];
+ pLast->GetPosition(pos);
+ pLast->GetRotation(rot);
+
+ Matrix mat(rot, pos);
+ mat.Translate(0, 0, pLast->GetPieceInfo()->m_fDimensions[2] - pLast->GetPieceInfo()->m_fDimensions[5]);
+ mat.GetTranslation(pos);
+ SnapPoint(&pos[0], &pos[1], &pos[2]);
+ pPiece->Initialize(pos[0], pos[1], pos[2], m_nCurStep, m_nCurFrame, m_nCurColor);
+ pPiece->ChangeKey(1, false, false, rot, PK_ROTATION);
+ pPiece->ChangeKey(1, true, false, rot, PK_ROTATION);
+ }
+ else
+ pPiece->Initialize(0, 0, 0, m_nCurStep, m_nCurFrame, m_nCurColor);
+
+ SelectAndFocusNone(false);
+ pPiece->CreateName(m_pPieces);
+ AddPiece(pPiece);
+ pPiece->CalculateConnections(m_pConnections, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, true, true);
+ pPiece->Focus();
+ SystemUpdateFocus(pPiece, LC_PIECE|LC_UPDATE_OBJECT|LC_UPDATE_TYPE);
+ UpdateSelection();
+ SystemPieceComboAdd(m_pCurPiece->m_strDescription);
+
+ if (m_nSnap & LC_DRAW_MOVE)
+ SetAction(LC_ACTION_MOVE);
+
+// AfxGetMainWnd()->PostMessage(WM_LC_UPDATE_INFO, (WPARAM)pNew, OT_PIECE);
+ SystemRedrawView();
+ SetModifiedFlag(true);
+ CheckPoint("Inserting");
+ } break;
+
+ case LC_PIECE_DELETE:
+ {
+ if (RemoveSelectedObjects())
+ {
+ SystemUpdateFocus(NULL, LC_UPDATE_OBJECT);
+ UpdateSelection();
+ SystemRedrawView();
+ SetModifiedFlag(true);
+ CheckPoint("Deleting");
+ }
+ } break;
+
+ case LC_PIECE_MINIFIG:
+ {
+ LC_MINIFIGDLG_OPTS opts;
+
+ if (SystemDoDialog(LC_DLG_MINIFIG, &opts))
+ {
+ SelectAndFocusNone(false);
+ int i;
+
+ for (i = 0; i < 15; i++)
+ {
+ if (opts.info[i] == NULL)
+ continue;
+
+ Matrix mat;
+ float rot[4];
+ Piece* pPiece = new Piece(opts.info[i]);
+
+ pPiece->Initialize(opts.pos[i][0], opts.pos[i][1], opts.pos[i][2], m_nCurStep, m_nCurFrame, opts.colors[i]);
+ pPiece->CreateName(m_pPieces);
+ AddPiece(pPiece);
+ pPiece->Select();
+
+ mat.CreateOld(0,0,0,opts.rot[i][0],opts.rot[i][1],opts.rot[i][2]);
+ mat.ToAxisAngle(rot);
+ pPiece->ChangeKey(1, false, false, rot, PK_ROTATION);
+ pPiece->ChangeKey(1, true, false, rot, PK_ROTATION);
+ pPiece->UpdatePosition(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation);
+ pPiece->CalculateConnections(m_pConnections, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, false, true);
+
+ SystemPieceComboAdd(opts.info[i]->m_strDescription);
+ }
+
+ float bs[6] = { 10000, 10000, 10000, -10000, -10000, -10000 };
+ int max = 0;
+
+ Group* pGroup;
+ for (pGroup = m_pGroups; pGroup; pGroup = pGroup->m_pNext)
+ if (strncmp (pGroup->m_strName, "Minifig #", 9) == 0)
+ if (sscanf(pGroup->m_strName, "Minifig #%d", &i) == 1)
+ if (i > max)
+ max = i;
+ pGroup = new Group;
+ sprintf(pGroup->m_strName, "Minifig #%0.2d", max+1);
+
+ pGroup->m_pNext = m_pGroups;
+ m_pGroups = pGroup;
+
+ for (Piece* pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ pPiece->SetGroup(pGroup);
+ pPiece->CompareBoundingBox(bs);
+ }
+
+ pGroup->m_fCenter[0] = (bs[0]+bs[3])/2;
+ pGroup->m_fCenter[1] = (bs[1]+bs[4])/2;
+ pGroup->m_fCenter[2] = (bs[2]+bs[5])/2;
+
+ SystemUpdateFocus(NULL, LC_UPDATE_OBJECT);
+ UpdateSelection();
+ SystemRedrawView();
+ SetModifiedFlag(true);
+ CheckPoint("Minifig");
+ }
+ } break;
+
+ case LC_PIECE_ARRAY:
+ {
+ LC_ARRAYDLG_OPTS opts;
+
+ if (SystemDoDialog(LC_DLG_ARRAY, &opts))
+ {
+ int total;
+
+ total = opts.n1DCount;
+ if (opts.nArrayDimension > 0)
+ total *= opts.n2DCount;
+ if (opts.nArrayDimension > 1)
+ total *= opts.n3DCount;
+
+ if (total < 2)
+ {
+ SystemDoMessageBox("Array only have 1 element.", LC_MB_OK|LC_MB_ICONWARNING);
+ break;
+ }
+
+ if ((m_nSnap & LC_DRAW_CM_UNITS) == 0)
+ {
+ opts.f2D[0] *= 0.8f;
+ opts.f2D[1] *= 0.8f;
+ opts.f2D[2] *= 0.96f;
+ opts.f3D[0] *= 0.8f;
+ opts.f3D[1] *= 0.8f;
+ opts.f3D[2] *= 0.96f;
+ opts.fMove[0] *= 0.8f;
+ opts.fMove[1] *= 0.8f;
+ opts.fMove[2] *= 0.96f;
+ }
+
+ Piece *pPiece, *pFirst, *pLast = NULL;
+ float bs[6] = { 10000, 10000, 10000, -10000, -10000, -10000 };
+ int sel = 0;
+ unsigned long i, j, k;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ pPiece->CompareBoundingBox(bs);
+ sel++;
+ }
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ if (!pPiece->IsSelected())
+ continue;
+
+ for (i = 0; i < opts.n1DCount; i++)
+ {
+ float pos[3], param[4];
+ pPiece->GetRotation(param);
+ pPiece->GetPosition(pos);
+ Matrix mat(param, pos);
+
+ if (sel == 1)
+ {
+ mat.GetTranslation(pos);
+ mat.Rotate(i*opts.fRotate[0], 1, 0, 0);
+ mat.Rotate(i*opts.fRotate[1], 0, 1, 0);
+ mat.Rotate(i*opts.fRotate[2], 0, 0, 1);
+ mat.SetTranslation(pos);
+ }
+ else
+ {
+ mat.RotateCenter(i*opts.fRotate[0],1,0,0,(bs[0]+bs[3])/2,(bs[1]+bs[4])/2,(bs[2]+bs[5])/2);
+ mat.RotateCenter(i*opts.fRotate[1],0,1,0,(bs[0]+bs[3])/2,(bs[1]+bs[4])/2,(bs[2]+bs[5])/2);
+ mat.RotateCenter(i*opts.fRotate[2],0,0,1,(bs[0]+bs[3])/2,(bs[1]+bs[4])/2,(bs[2]+bs[5])/2);
+ }
+ mat.ToAxisAngle(param);
+ mat.GetTranslation(pos);
+
+ if (i != 0)
+ {
+ if (pLast)
+ {
+ pLast->m_pNext = new Piece(pPiece->GetPieceInfo());
+ pLast = pLast->m_pNext;
+ }
+ else
+ pLast = pFirst = new Piece(pPiece->GetPieceInfo());
+
+ pLast->Initialize(pos[0]+i*opts.fMove[0], pos[1]+i*opts.fMove[1], pos[2]+i*opts.fMove[2],
+ m_nCurStep, m_nCurFrame, pPiece->GetColor());
+ pLast->ChangeKey(1, false, false, param, PK_ROTATION);
+ pLast->ChangeKey(1, true, false, param, PK_ROTATION);
+ }
+
+ if (opts.nArrayDimension == 0)
+ continue;
+
+ for (j = 0; j < opts.n2DCount; j++)
+ {
+ if (j != 0)
+ {
+ if (pLast)
+ {
+ pLast->m_pNext = new Piece(pPiece->GetPieceInfo());
+ pLast = pLast->m_pNext;
+ }
+ else
+ pLast = pFirst = new Piece(pPiece->GetPieceInfo());
+
+ pLast->Initialize(pos[0]+i*opts.fMove[0]+j*opts.f2D[0], pos[1]+i*opts.fMove[1]+j*opts.f2D[1], pos[2]+i*opts.fMove[2]+j*opts.f2D[2],
+ m_nCurStep, m_nCurFrame, pPiece->GetColor());
+ pLast->ChangeKey(1, false, false, param, PK_ROTATION);
+ pLast->ChangeKey(1, true, false, param, PK_ROTATION);
+ }
+
+ if (opts.nArrayDimension == 1)
+ continue;
+
+ for (k = 1; k < opts.n3DCount; k++)
+ {
+ if (pLast)
+ {
+ pLast->m_pNext = new Piece(pPiece->GetPieceInfo());
+ pLast = pLast->m_pNext;
+ }
+ else
+ pLast = pFirst = new Piece(pPiece->GetPieceInfo());
+
+ pLast->Initialize(pos[0]+i*opts.fMove[0]+j*opts.f2D[0]+k*opts.f3D[0], pos[1]+i*opts.fMove[1]+j*opts.f2D[1]+k*opts.f3D[1], pos[2]+i*opts.fMove[2]+j*opts.f2D[2]+k*opts.f3D[2],
+ m_nCurStep, m_nCurFrame, pPiece->GetColor());
+ pLast->ChangeKey(1, false, false, param, PK_ROTATION);
+ pLast->ChangeKey(1, true, false, param, PK_ROTATION);
+ }
+ }
+ }
+ }
+
+ while (pFirst)
+ {
+ pPiece = pFirst->m_pNext;
+ pFirst->CreateName(m_pPieces);
+ pFirst->UpdatePosition(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation);
+ AddPiece(pFirst);
+ pFirst->CalculateConnections(m_pConnections, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, false, true);
+ pFirst = pPiece;
+ }
+
+ SelectAndFocusNone(true);
+// SystemUpdateFocus(NULL, 255);
+ UpdateSelection();
+ SystemRedrawView();
+ SetModifiedFlag(true);
+ CheckPoint("Array");
+ }
+ } break;
+
+ case LC_PIECE_COPYKEYS:
+ {
+ float move[3], rot[4];
+ Piece* pPiece;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ pPiece->CalculatePositionRotation(m_bAnimation ? m_nCurStep : m_nCurFrame, !m_bAnimation, move, rot);
+ pPiece->ChangeKey(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, move, PK_POSITION);
+ pPiece->ChangeKey(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, rot, PK_ROTATION);
+ }
+
+ // TODO: cameras and lights
+
+ CalculateStep();
+ SystemRedrawView();
+ } break;
+
+ case LC_PIECE_GROUP:
+ {
+ Group* pGroup;
+ int i, max = 0;
+ char name[65];
+
+ for (pGroup = m_pGroups; pGroup; pGroup = pGroup->m_pNext)
+ if (strncmp (pGroup->m_strName, "Group #", 7) == 0)
+ if (sscanf(pGroup->m_strName, "Group #%d", &i) == 1)
+ if (i > max)
+ max = i;
+ sprintf(name, "Group #%0.2d", max+1);
+
+ if (SystemDoDialog(LC_DLG_GROUP, name))
+ {
+ pGroup = new Group();
+ strcpy(pGroup->m_strName, name);
+ pGroup->m_pNext = m_pGroups;
+ m_pGroups = pGroup;
+ float bs[6] = { 10000, 10000, 10000, -10000, -10000, -10000 };
+
+ for (Piece* pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ pPiece->DoGroup(pGroup);
+ pPiece->CompareBoundingBox(bs);
+ }
+
+ pGroup->m_fCenter[0] = (bs[0]+bs[3])/2;
+ pGroup->m_fCenter[1] = (bs[1]+bs[4])/2;
+ pGroup->m_fCenter[2] = (bs[2]+bs[5])/2;
+
+ RemoveEmptyGroups();
+ SetModifiedFlag(true);
+ CheckPoint("Grouping");
+ }
+ } break;
+
+ case LC_PIECE_UNGROUP:
+ {
+ Group* pList = NULL;
+ Group* pGroup;
+ Group* tmp;
+ Piece* pPiece;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ pGroup = pPiece->GetTopGroup();
+
+ // Check if we already removed the group
+ for (tmp = pList; tmp; tmp = tmp->m_pNext)
+ if (pGroup == tmp)
+ pGroup = NULL;
+
+ if (pGroup != NULL)
+ {
+ // First remove the group from the array
+ for (tmp = m_pGroups; tmp->m_pNext; tmp = tmp->m_pNext)
+ if (tmp->m_pNext == pGroup)
+ {
+ tmp->m_pNext = pGroup->m_pNext;
+ break;
+ }
+
+ if (pGroup == m_pGroups)
+ m_pGroups = pGroup->m_pNext;
+
+ // Now add it to the list of top groups
+ pGroup->m_pNext = pList;
+ pList = pGroup;
+ }
+ }
+
+ while (pList)
+ {
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ pPiece->UnGroup(pList);
+
+ pGroup = pList;
+ pList = pList->m_pNext;
+ delete pGroup;
+ }
+
+ RemoveEmptyGroups();
+ SetModifiedFlag(true);
+ CheckPoint("Ungrouping");
+ } break;
+
+ case LC_PIECE_GROUP_ADD:
+ {
+ Group* pGroup = NULL;
+ Piece* pPiece;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ pGroup = pPiece->GetTopGroup();
+ if (pGroup != NULL)
+ break;
+ }
+
+ if (pGroup != NULL)
+ {
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsFocused())
+ {
+ pPiece->SetGroup(pGroup);
+ break;
+ }
+ }
+
+ RemoveEmptyGroups();
+ SetModifiedFlag(true);
+ CheckPoint("Grouping");
+ } break;
+
+ case LC_PIECE_GROUP_REMOVE:
+ {
+ Piece* pPiece;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsFocused())
+ {
+ pPiece->UnGroup(NULL);
+ break;
+ }
+
+ RemoveEmptyGroups();
+ SetModifiedFlag(true);
+ CheckPoint("Ungrouping");
+ } break;
+
+ case LC_PIECE_GROUP_EDIT:
+ {
+ int i;
+ Group* pGroup;
+ Piece* pPiece;
+ LC_GROUPEDITDLG_OPTS opts;
+
+ for (i = 0, pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ i++;
+ opts.piececount = i;
+ opts.pieces = (Piece**)malloc(i*sizeof(Piece*));
+ opts.piecesgroups = (Group**)malloc(i*sizeof(Group*));
+
+ for (i = 0, pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext, i++)
+ {
+ opts.pieces[i] = pPiece;
+ opts.piecesgroups[i] = pPiece->GetGroup();
+ }
+
+ for (i = 0, pGroup = m_pGroups; pGroup; pGroup = pGroup->m_pNext)
+ i++;
+ opts.groupcount = i;
+ opts.groups = (Group**)malloc(i*sizeof(Group*));
+ opts.groupsgroups = (Group**)malloc(i*sizeof(Group*));
+
+ for (i = 0, pGroup = m_pGroups; pGroup; pGroup = pGroup->m_pNext, i++)
+ {
+ opts.groups[i] = pGroup;
+ opts.groupsgroups[i] = pGroup->m_pGroup;
+ }
+
+ if (SystemDoDialog(LC_DLG_EDITGROUPS, &opts))
+ {
+ for (i = 0; i < opts.piececount; i++)
+ opts.pieces[i]->SetGroup(opts.piecesgroups[i]);
+
+ for (i = 0; i < opts.groupcount; i++)
+ opts.groups[i]->m_pGroup = opts.groupsgroups[i];
+
+ RemoveEmptyGroups();
+ SelectAndFocusNone(false);
+ SystemUpdateFocus(NULL, 0);
+ UpdateSelection();
+ SystemRedrawView();
+ SetModifiedFlag(true);
+ CheckPoint("Editing");
+ }
+
+ free(opts.pieces);
+ free(opts.piecesgroups);
+ free(opts.groups);
+ free(opts.groupsgroups);
+ } break;
+
+ case LC_PIECE_HIDE_SELECTED:
+ {
+ Piece* pPiece;
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ pPiece->Hide();
+ UpdateSelection();
+ SystemUpdateFocus(NULL, LC_UPDATE_OBJECT);
+ SystemRedrawView();
+ } break;
+
+ case LC_PIECE_HIDE_UNSELECTED:
+ {
+ Piece* pPiece;
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (!pPiece->IsSelected())
+ pPiece->Hide();
+ UpdateSelection();
+ SystemRedrawView();
+ } break;
+
+ case LC_PIECE_UNHIDE_ALL:
+ {
+ Piece* pPiece;
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ pPiece->UnHide();
+ UpdateSelection();
+ SystemRedrawView();
+ } break;
+
+ case LC_PIECE_PREVIOUS:
+ {
+ bool redraw = false;
+
+ for (Piece* pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ if (m_bAnimation)
+ {
+ unsigned short t = pPiece->GetFrameShow();
+ if (t > 1)
+ {
+ redraw = true;
+ pPiece->SetFrameShow(t-1);
+ }
+ }
+ else
+ {
+ unsigned char t = pPiece->GetStepShow();
+ if (t > 1)
+ {
+ redraw = true;
+ pPiece->SetStepShow(t-1);
+ }
+ }
+ }
+
+ if (redraw)
+ {
+ SetModifiedFlag(true);
+ CheckPoint("Modifying");
+ SystemRedrawView();
+ }
+ } break;
+
+ case LC_PIECE_NEXT:
+ {
+ bool redraw = false;
+
+ for (Piece* pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ if (m_bAnimation)
+ {
+ unsigned short t = pPiece->GetFrameShow();
+ if (t < m_nTotalFrames)
+ {
+ redraw = true;
+ pPiece->SetFrameShow(t+1);
+ }
+ }
+ else
+ {
+ unsigned char t = pPiece->GetStepShow();
+ if (t < 255)
+ {
+ redraw = true;
+ pPiece->SetStepShow(t+1);
+ }
+ }
+ }
+
+ if (redraw)
+ {
+ SetModifiedFlag(true);
+ CheckPoint("Modifying");
+ SystemRedrawView();
+ }
+ } break;
+
+ case LC_VIEW_PREFERENCES:
+ {
+ LC_PREFERENCESDLG_OPTS opts;
+ opts.nMouse = m_nMouse;
+ opts.nSaveInterval = m_nAutosave;
+ strcpy(opts.strPath, m_strModelsPath);
+ opts.nDetail = m_nDetail;
+ opts.fLineWidth = m_fLineWidth;
+ opts.nSnap = m_nSnap;
+ opts.nAngleSnap = m_nAngleSnap;
+ opts.nGridSize = m_nGridSize;
+ opts.nScene = m_nScene;
+ opts.fDensity = m_fFogDensity;
+ strcpy(opts.strBackground, m_strBackground);
+ memcpy(opts.fBackground, m_fBackground, sizeof(m_fBackground));
+ memcpy(opts.fFog, m_fFogColor, sizeof(m_fFogColor));
+ memcpy(opts.fAmbient, m_fAmbient, sizeof(m_fAmbient));
+ memcpy(opts.fGrad1, m_fGradient1, sizeof(m_fGradient1));
+ memcpy(opts.fGrad2, m_fGradient2, sizeof(m_fGradient2));
+ strcpy(opts.strFooter, m_strFooter);
+ strcpy(opts.strHeader, m_strHeader);
+
+ if (SystemDoDialog(LC_DLG_PREFERENCES, &opts))
+ {
+ m_nMouse = opts.nMouse;
+ m_nAutosave = opts.nSaveInterval;
+ strcpy(m_strModelsPath, opts.strPath);
+ m_nDetail = opts.nDetail;
+ m_fLineWidth = opts.fLineWidth;
+ m_nSnap = opts.nSnap;
+ m_nAngleSnap = opts.nAngleSnap;
+ m_nGridSize = opts.nGridSize;
+ m_nScene = opts.nScene;
+ m_fFogDensity = opts.fDensity;
+ strcpy(m_strBackground, opts.strBackground);
+ memcpy(m_fBackground, opts.fBackground, sizeof(m_fBackground));
+ memcpy(m_fFogColor, opts.fFog, sizeof(m_fFogColor));
+ memcpy(m_fAmbient, opts.fAmbient, sizeof(m_fAmbient));
+ memcpy(m_fGradient1, opts.fGrad1, sizeof(m_fGradient1));
+ memcpy(m_fGradient2, opts.fGrad2, sizeof(m_fGradient2));
+ strcpy(m_strFooter, opts.strFooter);
+ strcpy(m_strHeader, opts.strHeader);
+ SystemUpdateSnap(m_nSnap);
+
+ RenderInitialize();
+ SystemRedrawView();
+ }
+ } break;
+
+ case LC_VIEW_ZOOMIN:
+ {
+ m_pViewCameras[m_nActiveViewport]->DoZoom(-1, m_nMouse, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys);
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+ } break;
+
+ case LC_VIEW_ZOOMOUT:
+ {
+ m_pViewCameras[m_nActiveViewport]->DoZoom(1, m_nMouse, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys);
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+ } break;
+
+ case LC_VIEW_ZOOMEXTENTS:
+ {
+ if (m_pPieces == 0) break;
+
+ bool bControl = IsKeyDown(KEY_CONTROL);
+ double modelMatrix[16], projMatrix[16];
+ float up[3], eye[3], target[3];
+ float bs[6] = { 10000, 10000, 10000, -10000, -10000, -10000 };
+ int viewport[4], out, x, y, w, h;
+
+ for (Piece* pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation))
+ pPiece->CompareBoundingBox(bs);
+
+ float v[24] = {
+ bs[0], bs[1], bs[5],
+ bs[3], bs[1], bs[5],
+ bs[0], bs[1], bs[2],
+ bs[3], bs[4], bs[5],
+ bs[3], bs[4], bs[2],
+ bs[0], bs[4], bs[2],
+ bs[0], bs[4], bs[5],
+ bs[3], bs[1], bs[2] };
+
+ for (int vp = 0; vp < viewports[m_nViewportMode].n; vp++)
+ {
+ Camera* pCam;
+ if (bControl)
+ pCam = m_pViewCameras[vp];
+ else
+ pCam = m_pViewCameras[m_nActiveViewport];
+
+ if (!bControl)
+ vp = m_nActiveViewport;
+
+ x = (int)(viewports[m_nViewportMode].dim[m_nActiveViewport][0] * (float)m_nViewX);
+ y = (int)(viewports[m_nViewportMode].dim[m_nActiveViewport][1] * (float)m_nViewY);
+ w = (int)(viewports[m_nViewportMode].dim[m_nActiveViewport][2] * (float)m_nViewX);
+ h = (int)(viewports[m_nViewportMode].dim[m_nActiveViewport][3] * (float)m_nViewY);
+ float ratio = (float)w/h;
+
+ glViewport(x,y,w,h);
+ pCam->LoadProjection(ratio);
+
+ if (!bControl)
+ vp = 4;
+
+ pCam->GetTarget(target);
+ pCam->GetEye(eye);
+
+ up[0] = (bs[0] + bs[3])/2 - target[0];
+ up[1] = (bs[1] + bs[4])/2 - target[1];
+ up[2] = (bs[2] + bs[5])/2 - target[2];
+
+ if (pCam->IsSide())
+ {
+ eye[0] += up[0];
+ eye[1] += up[1];
+ eye[2] += up[2];
+ }
+ target[0] += up[0];
+ target[1] += up[1];
+ target[2] += up[2];
+
+ pCam->GetUp(up);
+ Vector upvec(up), frontvec(eye[0]-target[0], eye[1]-target[1], eye[2]-target[2]), sidevec;
+ frontvec.Normalize();
+ sidevec.Cross(frontvec, upvec);
+ upvec.Cross(sidevec, frontvec);
+ upvec.Normalize();
+ upvec.ToFloat(up);
+ frontvec.Scale(0.25f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glGetDoublev(GL_PROJECTION_MATRIX,projMatrix);
+ glGetIntegerv(GL_VIEWPORT,viewport);
+
+ for (out = 0; out < 10000; out++) // Zoom in
+ {
+ eye[0] -= frontvec.X();
+ eye[1] -= frontvec.Y();
+ eye[2] -= frontvec.Z();
+ glLoadIdentity();
+ gluLookAt(eye[0], eye[1], eye[2], target[0], target[1], target[2], up[0], up[1], up[2]);
+ glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
+
+ for (int i = 0; i < 24; i+=3)
+ {
+ double winx, winy, winz;
+ gluProject (v[i], v[i+1], v[i+2], modelMatrix, projMatrix, viewport, &winx, &winy, &winz);
+ if ((winx < viewport[0] + 1) || (winy < viewport[1] + 1) ||
+ (winx > viewport[0] + viewport[2] - 1) || (winy > viewport[1] + viewport[3] - 1))
+ {
+ out = 10000;
+ continue;
+ }
+ }
+ }
+
+ bool stp = false;
+ for (out = 0; out < 10000 && !stp; out++) // zoom out
+ {
+ stp = true;
+ eye[0] += frontvec.X();
+ eye[1] += frontvec.Y();
+ eye[2] += frontvec.Z();
+ glLoadIdentity();
+ gluLookAt(eye[0], eye[1], eye[2], target[0], target[1], target[2], up[0], up[1], up[2]);
+ glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
+
+ for (int i = 0; i < 24; i+=3)
+ {
+ double winx, winy, winz;
+ gluProject (v[i], v[i+1], v[i+2], modelMatrix, projMatrix, viewport, &winx, &winy, &winz);
+ if ((winx < viewport[0] + 1) || (winy < viewport[1] + 1) ||
+ (winx > viewport[0] + viewport[2] - 1) || (winy > viewport[1] + viewport[3] - 1))
+ {
+ stp = false;
+ continue;
+ }
+ }
+ }
+
+ pCam->ChangeKey(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, eye, CK_EYE);
+ pCam->ChangeKey(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, target, CK_TARGET);
+ pCam->UpdatePosition(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation);
+ }
+
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+ } break;
+
+ case LC_VIEW_VIEWPORTS:
+ {
+ // Safety check
+ if (m_nActiveViewport >= viewports[nParam].n)
+ {
+ SystemUpdateCurrentCamera(m_pViewCameras[m_nActiveViewport], m_pViewCameras[0], m_pCameras);
+ m_nActiveViewport = 0;
+ }
+
+ SystemUpdateViewport(nParam, m_nViewportMode);
+ m_nViewportMode = (unsigned char)nParam;
+ SystemRedrawView();
+ } break;
+
+ case LC_VIEW_STEP_NEXT:
+ {
+ if (m_bAnimation)
+ m_nCurFrame++;
+ else
+ m_nCurStep++;
+
+ CalculateStep();
+ UpdateSelection();
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+
+ if (m_bAnimation)
+ SystemUpdateTime(m_bAnimation, m_nCurFrame, m_nTotalFrames);
+ else
+ SystemUpdateTime(m_bAnimation, m_nCurStep, 255);
+ } break;
+
+ case LC_VIEW_STEP_PREVIOUS:
+ {
+ if (m_bAnimation)
+ m_nCurFrame--;
+ else
+ m_nCurStep--;
+
+ CalculateStep();
+ UpdateSelection();
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+
+ if (m_bAnimation)
+ SystemUpdateTime(m_bAnimation, m_nCurFrame, m_nTotalFrames);
+ else
+ SystemUpdateTime(m_bAnimation, m_nCurStep, 255);
+ } break;
+
+ case LC_VIEW_STEP_FIRST:
+ {
+ if (m_bAnimation)
+ m_nCurFrame = 1;
+ else
+ m_nCurStep = 1;
+
+ CalculateStep();
+ UpdateSelection();
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+
+ if (m_bAnimation)
+ SystemUpdateTime(m_bAnimation, m_nCurFrame, m_nTotalFrames);
+ else
+ SystemUpdateTime(m_bAnimation, m_nCurStep, 255);
+ } break;
+
+ case LC_VIEW_STEP_LAST:
+ {
+ if (m_bAnimation)
+ m_nCurFrame = m_nTotalFrames;
+ else
+ m_nCurStep = 255;
+
+ CalculateStep();
+ UpdateSelection();
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+
+ if (m_bAnimation)
+ SystemUpdateTime(m_bAnimation, m_nCurFrame, m_nTotalFrames);
+ else
+ SystemUpdateTime(m_bAnimation, m_nCurStep, 255);
+ } break;
+
+ case LC_VIEW_STEP_CHOOSE:
+ {
+ SystemDoDialog(LC_DLG_STEPCHOOSE, NULL);
+ if (m_bAnimation)
+ SystemUpdateTime(m_bAnimation, m_nCurFrame, m_nTotalFrames);
+ else
+ SystemUpdateTime(m_bAnimation, m_nCurStep, 255);
+ } break;
+
+ case LC_VIEW_STEP_SET:
+ {
+ if (IsDrawing())
+ break;
+
+ if (m_bAnimation)
+ m_nCurFrame = (nParam < m_nTotalFrames) ? (unsigned short)nParam : m_nTotalFrames;
+ else
+ m_nCurStep = (nParam < 255) ? (unsigned char)nParam : 255;
+
+ CalculateStep();
+ UpdateSelection();
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+
+ if (m_bAnimation)
+ SystemUpdateTime(m_bAnimation, m_nCurFrame, m_nTotalFrames);
+ else
+ SystemUpdateTime(m_bAnimation, m_nCurStep, 255);
+ } break;
+
+ case LC_VIEW_STOP:
+ {
+ m_bStopRender = true;
+ } break;
+
+ case LC_VIEW_PLAY:
+ {
+ SelectAndFocusNone(false);
+ UpdateSelection();
+ m_bStopRender = false;
+ m_bRendering = true;
+ SystemUpdatePlay(false, true);
+ long time = SystemGetTicks();
+ unsigned short tics;
+ float rate = 1000.0f/m_nFPS;
+
+ while (!m_bStopRender)
+ {
+ tics = (unsigned short)(SystemGetTicks() - time);
+ if (tics < rate)
+ continue; // nothing to do
+
+ time = SystemGetTicks();
+ m_nCurFrame += (unsigned short)((float)tics/rate);
+ while (m_nCurFrame > m_nTotalFrames)
+ m_nCurFrame -= m_nTotalFrames;
+ CalculateStep();
+ SystemUpdateTime(true, m_nCurFrame, m_nTotalFrames);
+ RenderScene((m_nDetail & LC_DET_FAST) == 0, true);
+ SystemSwapBuffers();
+ SystemPumpMessages();
+ }
+ m_bRendering = false;
+ SystemUpdatePlay(true, false);
+ SystemUpdateFocus(NULL, 0);
+ } break;
+
+ case LC_VIEW_CAMERA_MENU:
+ {
+ Camera* pCamera = m_pCameras;
+
+ while (nParam--)
+ pCamera = pCamera->m_pNext;
+
+ SystemUpdateCurrentCamera(m_pViewCameras[m_nActiveViewport], pCamera, m_pCameras);
+ m_pViewCameras[m_nActiveViewport] = pCamera;
+ SystemRedrawView();
+ } break;
+
+ case LC_VIEW_CAMERA_RESET:
+ {
+ Camera* pCamera;
+ int i;
+
+ while (m_pCameras)
+ {
+ pCamera = m_pCameras;
+ m_pCameras = m_pCameras->m_pNext;
+ delete pCamera;
+ }
+
+ for (m_pCameras = pCamera = NULL, i = 0; i < 7; i++)
+ {
+ pCamera = new Camera(i, pCamera);
+ if (m_pCameras == NULL)
+ m_pCameras = pCamera;
+
+ switch (i)
+ {
+ case LC_CAMERA_MAIN: m_pViewCameras[0] = pCamera; break;
+ case LC_CAMERA_FRONT: m_pViewCameras[1] = pCamera; break;
+ case LC_CAMERA_TOP: m_pViewCameras[2] = pCamera; break;
+ case LC_CAMERA_RIGHT: m_pViewCameras[3] = pCamera; break;
+ }
+ }
+
+ SystemUpdateCameraMenu(m_pCameras);
+ SystemUpdateCurrentCamera(NULL, m_pViewCameras[m_nActiveViewport], m_pCameras);
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+ SetModifiedFlag(true);
+ CheckPoint("Reset Cameras");
+ } break;
+
+ case LC_VIEW_AUTOPAN:
+ {
+ if (IsDrawing())
+ break;
+
+ short x = (short)nParam;
+ short y = (short)((nParam >> 16) & 0xFFFF);
+
+ x -= x > 0 ? 5 : -5;
+ y -= y > 0 ? 5 : -5;
+
+ m_pViewCameras[m_nActiveViewport]->DoPan(x/4, y/4, 1, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys);
+ m_nDownX = x;
+ m_nDownY = y;
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+ } break;
+
+ case LC_HELP_ABOUT:
+ {
+ SystemDoDialog(LC_DLG_ABOUT, 0);
+ } break;
+
+ case LC_TOOLBAR_ANIMATION:
+ {
+ m_bAnimation = !m_bAnimation;
+
+ CalculateStep();
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+
+ SystemUpdateAnimation(m_bAnimation, m_bAddKeys);
+ if (m_bAnimation)
+ SystemUpdateTime(m_bAnimation, m_nCurFrame, m_nTotalFrames);
+ else
+ SystemUpdateTime(m_bAnimation, m_nCurStep, 255);
+ } break;
+
+ case LC_TOOLBAR_ADDKEYS:
+ {
+ m_bAddKeys = !m_bAddKeys;
+ SystemUpdateAnimation(m_bAnimation, m_bAddKeys);
+ } break;
+
+ // Change snap X, Y, Z, All, None or Angle.
+ case LC_TOOLBAR_SNAPMENU:
+ {
+ switch (nParam)
+ {
+ case 0:
+ if (m_nSnap & LC_DRAW_SNAP_X)
+ m_nSnap &= ~LC_DRAW_SNAP_X;
+ else
+ m_nSnap |= LC_DRAW_SNAP_X;
+ break;
+ case 1:
+ if (m_nSnap & LC_DRAW_SNAP_Y)
+ m_nSnap &= ~LC_DRAW_SNAP_Y;
+ else
+ m_nSnap |= LC_DRAW_SNAP_Y;
+ break;
+ case 2:
+ if (m_nSnap & LC_DRAW_SNAP_Z)
+ m_nSnap &= ~LC_DRAW_SNAP_Z;
+ else
+ m_nSnap |= LC_DRAW_SNAP_Z;
+ break;
+ case 3:
+ m_nSnap |= LC_DRAW_SNAP_X | LC_DRAW_SNAP_Y | LC_DRAW_SNAP_Z;
+ break;
+ case 4:
+ m_nSnap &= ~(LC_DRAW_SNAP_X | LC_DRAW_SNAP_Y | LC_DRAW_SNAP_Z);
+ break;
+ case 5:
+ if (m_nSnap & LC_DRAW_SNAP_A)
+ m_nSnap &= ~LC_DRAW_SNAP_A;
+ else
+ m_nSnap |= LC_DRAW_SNAP_A;
+ break;
+ }
+ SystemUpdateSnap(m_nSnap);
+ } break;
+
+ case LC_TOOLBAR_LOCKMENU:
+ {
+ switch (nParam)
+ {
+ case 0:
+ if (m_nSnap & LC_DRAW_LOCK_X)
+ m_nSnap &= ~LC_DRAW_LOCK_X;
+ else
+ m_nSnap |= LC_DRAW_LOCK_X;
+ break;
+ case 1:
+ if (m_nSnap & LC_DRAW_LOCK_Y)
+ m_nSnap &= ~LC_DRAW_LOCK_Y;
+ else
+ m_nSnap |= LC_DRAW_LOCK_Y;
+ break;
+ case 2:
+ if (m_nSnap & LC_DRAW_LOCK_Z)
+ m_nSnap &= ~LC_DRAW_LOCK_Z;
+ else
+ m_nSnap |= LC_DRAW_LOCK_Z;
+ break;
+ case 3:
+ m_nSnap &= ~(LC_DRAW_LOCK_X|LC_DRAW_LOCK_Y|LC_DRAW_LOCK_Z);
+ break;
+ case 4:
+ m_nSnap &= ~LC_DRAW_3DMOUSE;
+ break;
+ case 5:
+ m_nSnap |= LC_DRAW_3DMOUSE;
+ break;
+ }
+ SystemUpdateSnap(m_nSnap);
+ } break;
+
+ case LC_TOOLBAR_SNAPMOVEMENU:
+ {
+ if (nParam < 11)
+ m_nMoveSnap = (unsigned short)nParam;
+ else
+ {
+ if (nParam == 19)
+ m_nMoveSnap = 100;
+ else
+ m_nMoveSnap = (unsigned short)(nParam - 10)*5 + 10;
+ }
+ SystemUpdateMoveSnap(m_nMoveSnap);
+ } break;
+
+ case LC_TOOLBAR_BACKGROUND:
+ case LC_TOOLBAR_FASTRENDER:
+ {
+ if (id == LC_TOOLBAR_BACKGROUND)
+ {
+ if (m_nDetail & LC_DET_BACKGROUND)
+ m_nDetail &= ~LC_DET_BACKGROUND;
+ else
+ m_nDetail |= LC_DET_BACKGROUND;
+ }
+ else
+ {
+ if (m_nDetail & LC_DET_FAST)
+ m_nDetail &= ~(LC_DET_FAST | LC_DET_BACKGROUND);
+ else
+ m_nDetail |= LC_DET_FAST;
+ SystemRedrawView();
+ }
+
+ SystemUpdateRenderingMode((m_nDetail & LC_DET_BACKGROUND) != 0, (m_nDetail & LC_DET_FAST) != 0);
+ } break;
+ }
+}
+
+void Project::SetAction(int nAction)
+{
+ SystemUpdateAction(nAction, m_nCurAction);
+ m_nCurAction = nAction;
+}
+
+// Remove unused groups
+void Project::RemoveEmptyGroups()
+{
+ bool recurse = false;
+ Group *g1, *g2;
+ Piece* pPiece;
+ int ref;
+
+ for (g1 = m_pGroups; g1;)
+ {
+ ref = 0;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->GetGroup() == g1)
+ ref++;
+
+ for (g2 = m_pGroups; g2; g2 = g2->m_pNext)
+ if (g2->m_pGroup == g1)
+ ref++;
+
+ if (ref < 2)
+ {
+ if (ref != 0)
+ {
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->GetGroup() == g1)
+ pPiece->SetGroup(g1->m_pGroup);
+
+ for (g2 = m_pGroups; g2; g2 = g2->m_pNext)
+ if (g2->m_pGroup == g1)
+ g2->m_pGroup = g1->m_pGroup;
+ }
+
+ if (g1 == m_pGroups)
+ {
+ m_pGroups = g1->m_pNext;
+ delete g1;
+ g1 = m_pGroups;
+ }
+ else
+ {
+ for (g2 = m_pGroups; g2; g2 = g2->m_pNext)
+ if (g2->m_pNext == g1)
+ {
+ g2->m_pNext = g1->m_pNext;
+ break;
+ }
+
+ delete g1;
+ g1 = g2->m_pNext;
+ }
+
+ recurse = true;
+ }
+ else
+ g1 = g1->m_pNext;
+ }
+
+ if (recurse)
+ RemoveEmptyGroups();
+}
+
+void Project::SelectAndFocusNone(bool bFocusOnly)
+{
+ Piece* pPiece;
+ Camera* pCamera;
+ Light* pLight;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (bFocusOnly)
+ pPiece->UnFocus();
+ else
+ pPiece->UnSelect();
+
+ for (pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ if (bFocusOnly)
+ pCamera->UnFocus();
+ else
+ pCamera->UnSelect();
+
+ for (pLight = m_pLights; pLight; pLight = pLight->m_pNext)
+ if (bFocusOnly)
+ pLight->UnFocus();
+ else
+ pLight->UnSelect();
+
+// AfxGetMainWnd()->PostMessage(WM_LC_UPDATE_INFO, NULL, OT_PIECE);
+}
+
+PieceInfo* Project::GetPieceInfo(int index)
+{
+ return &m_pPieceIdx[index];
+}
+
+Camera* Project::GetCamera(int i)
+{
+ Camera* pCamera;
+
+ for (pCamera = m_pCameras; i--, pCamera; pCamera = pCamera->m_pNext)
+ ;
+ return pCamera;
+}
+
+void Project::GetFocusPosition(float* pos)
+{
+ Piece* pPiece;
+ Camera* pCamera;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsFocused())
+ {
+ pPiece->GetPosition(pos);
+ if ((m_nSnap & LC_DRAW_CM_UNITS) == 0)
+ {
+ pos[0] /= 0.8f;
+ pos[1] /= 0.8f;
+ pos[2] /= 0.96f;
+ }
+ return;
+ }
+
+ for (pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ {
+ if (pCamera->IsEyeFocused())
+ {
+ pCamera->GetEye(pos);
+ if ((m_nSnap & LC_DRAW_CM_UNITS) == 0)
+ {
+ pos[0] /= 0.8f;
+ pos[1] /= 0.8f;
+ pos[2] /= 0.96f;
+ }
+ return;
+ }
+
+ if (pCamera->IsTargetFocused())
+ {
+ pCamera->GetTarget(pos);
+ if ((m_nSnap & LC_DRAW_CM_UNITS) == 0)
+ {
+ pos[0] /= 0.8f;
+ pos[1] /= 0.8f;
+ pos[2] /= 0.96f;
+ }
+ return;
+ }
+ }
+
+ // TODO: light
+
+ pos[0] = pos[1] = pos[2] = 0.0f;
+}
+
+Texture* Project::FindTexture(char* name)
+{
+ for (int i = 0; i < m_nTextureCount; i++)
+ if (!strcmp (name, m_pTextures[i].m_strName))
+ return &m_pTextures[i];
+
+ return NULL;
+}
+
+// Remeber to make 'name' uppercase.
+PieceInfo* Project::FindPieceInfo(char* name)
+{
+ PieceInfo* pInfo;
+ int i;
+
+ for (i = m_nPieceCount, pInfo = m_pPieceIdx; i--; pInfo++)
+ if (!strcmp (name, pInfo->m_strName))
+ return pInfo;
+
+ for (i = 0; i < m_nMovedCount; i++)
+ if (!strcmp(&m_pMovedReference[i*18], name))
+ {
+ char* tmp = &m_pMovedReference[i*18+9];
+
+ for (i = m_nPieceCount, pInfo = m_pPieceIdx; i--; pInfo++)
+ if (!strcmp (tmp, pInfo->m_strName))
+ return pInfo;
+
+ break; // something went wrong.
+ }
+
+ return NULL;
+}
+
+BoundingBox* Project::FindObjectFromPoint(int x, int y)
+{
+ double px, py, pz, rx, ry, rz;
+ double modelMatrix[16], projMatrix[16];
+ int viewport[4];
+ Piece* pPiece;
+ Camera* pCamera;
+ Light* pLight;
+
+ LoadViewportProjection();
+ glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
+ glGetDoublev(GL_PROJECTION_MATRIX,projMatrix);
+ glGetIntegerv(GL_VIEWPORT,viewport);
+
+ // Unproject the selected point against both the front and the back clipping plane
+ gluUnProject(x, y, 0, modelMatrix, projMatrix, viewport, &px, &py, &pz);
+ gluUnProject(x, y, 1, modelMatrix, projMatrix, viewport, &rx, &ry, &rz);
+
+ CLICKLINE ClickLine = { px, py, pz, rx-px, ry-py, rz-pz, DBL_MAX, NULL };
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation))
+ pPiece->MinIntersectDist(&ClickLine);
+
+ for (pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ if (pCamera != m_pViewCameras[m_nActiveViewport])
+ pCamera->MinIntersectDist(&ClickLine);
+
+ for (pLight = m_pLights; pLight; pLight = pLight->m_pNext)
+ pLight->MinIntersectDist(&ClickLine);
+
+ return ClickLine.pClosest;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Mouse handling
+
+// Returns true if point is not inside the current viewport.
+bool Project::SetActiveViewport(int px, int py)
+{
+ float x, y, w, h;
+ int vp;
+
+ for (vp = 0; vp < viewports[m_nViewportMode].n; vp++)
+ {
+ x = viewports[m_nViewportMode].dim[vp][0] * (float)m_nViewX;
+ y = viewports[m_nViewportMode].dim[vp][1] * (float)m_nViewY;
+ w = viewports[m_nViewportMode].dim[vp][2] * (float)m_nViewX;
+ h = viewports[m_nViewportMode].dim[vp][3] * (float)m_nViewY;
+
+ if (px > x && px < x + w && py > y && py < y + h)
+ {
+ if (m_nActiveViewport != vp)
+ {
+ SystemUpdateCurrentCamera(m_pViewCameras[m_nActiveViewport], m_pViewCameras[vp], m_pCameras);
+ m_nActiveViewport = vp;
+ Render(false);
+// glDrawBuffer(GL_FRONT);
+// RenderViewports(true);
+// glDrawBuffer(GL_BACK);
+
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+
+ // We shouldn't get here...
+ m_nActiveViewport = 0;
+ return true;
+}
+
+void Project::LoadViewportProjection()
+{
+ int x, y, w, h;
+ float ratio;
+
+ x = (int)(viewports[m_nViewportMode].dim[m_nActiveViewport][0] * (float)m_nViewX);
+ y = (int)(viewports[m_nViewportMode].dim[m_nActiveViewport][1] * (float)m_nViewY);
+ w = (int)(viewports[m_nViewportMode].dim[m_nActiveViewport][2] * (float)m_nViewX);
+ h = (int)(viewports[m_nViewportMode].dim[m_nActiveViewport][3] * (float)m_nViewY);
+
+ ratio = (float)w/h;
+ glViewport(x, y, w, h);
+ m_pViewCameras[m_nActiveViewport]->LoadProjection(ratio);
+}
+
+// Returns true if the mouse was being tracked.
+bool Project::StopTracking(bool bAccept)
+{
+ if (m_nTracking == LC_TRACK_NONE)
+ return false;
+
+ if ((m_nTracking == LC_TRACK_START_LEFT) || (m_nTracking == LC_TRACK_START_RIGHT))
+ {
+ if (m_pTrackFile)
+ {
+ delete m_pTrackFile;
+ m_pTrackFile = NULL;
+ }
+
+ m_nTracking = LC_TRACK_NONE;
+ SystemReleaseMouse();
+ return false;
+ }
+
+ m_bTrackCancel = true;
+ m_nTracking = LC_TRACK_NONE;
+ SystemReleaseMouse();
+
+ if (bAccept)
+ {
+ if (m_nCurAction == LC_ACTION_CAMERA)
+ {
+ SystemUpdateCameraMenu(m_pCameras);
+ SystemUpdateCurrentCamera(NULL, m_pViewCameras[m_nActiveViewport], m_pCameras);
+ SetModifiedFlag(true);
+ CheckPoint("Inserting");
+ }
+ }
+ else if (m_pTrackFile != NULL)
+ {
+ DeleteContents(true);
+ FileLoad(m_pTrackFile, true, false);
+ }
+
+ return true;
+/*
+ POINT pt;
+ GetCursorPos(&pt);
+ ScreenToClient(&pt);
+ if (bAccept)
+ {
+// ADD CHECKPOINT()
+
+ if ((m_nCurAction == ACTION_ZOOM_REGION) && (m_ptTrack != pt))
+ {
+// int m_nDownX;
+// int m_nDownY;
+ Camera* pCam = m_pViewCameras[m_nActiveViewport];
+
+ int out;
+ double modelMatrix[16], projMatrix[16], Pos[3] = { 0,0,0 };
+ int viewport[4];
+ float eye[3], target[3], up[3];
+ memcpy(eye, pCam->m_fEye, sizeof(eye));
+ memcpy(target, pCam->m_fTarget, sizeof(target));
+ memcpy(up, pCam->m_fUp, sizeof(up));
+ double obj1x,obj1y,obj1z, obj2x,obj2y,obj2z;
+ int x = (int)(viewports[m_nActiveViewport].dim[m_nActiveViewport][0] * ((float)m_szView.cx));
+ int y = (int)(viewports[m_nActiveViewport].dim[m_nActiveViewport][1] * ((float)m_szView.cy));
+ int w = (int)(viewports[m_nActiveViewport].dim[m_nActiveViewport][2] * ((float)m_szView.cx));
+ int h = (int)(viewports[m_nActiveViewport].dim[m_nActiveViewport][3] * ((float)m_szView.cy));
+
+ POINT pt1, pt2;
+ pt1.x = pt.x;
+ pt1.y = m_szView.cy - pt.y - 1;
+ pt2.x = m_ptTrack.x;
+ pt2.y = m_szView.cy - m_ptTrack.y - 1;
+
+ if (pt1.x < x) pt1.x = x;
+ if (pt1.y < y) pt1.y = y;
+ if (pt1.x > x+w) pt1.x = x+w;
+ if (pt1.y > y+h) pt1.y = y+h;
+
+ float ratio = (float)w/h;
+ glViewport(x,y,w,h);
+ pCam->LoadProjection(ratio);
+
+ glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
+ glGetDoublev(GL_PROJECTION_MATRIX,projMatrix);
+ glGetIntegerv(GL_VIEWPORT,viewport);
+
+ double line[4][3];
+ gluUnProject((double)pt1.x,(double)pt1.y,0.9,modelMatrix,projMatrix,viewport,&line[0][0],&line[0][1],&line[0][2]);
+ gluUnProject((double)pt1.x,(double)pt1.y,0,modelMatrix,projMatrix,viewport,&line[1][0],&line[1][1],&line[1][2]);
+ gluUnProject((double)pt2.x,(double)pt2.y,0.9,modelMatrix,projMatrix,viewport,&line[2][0],&line[2][1],&line[2][2]);
+ gluUnProject((double)pt2.x,(double)pt2.y,0,modelMatrix,projMatrix,viewport,&line[3][0],&line[3][1],&line[3][2]);
+
+// pCam->GetPosition(m_nCurStep, &eye[0],&target[0],&up[0]);
+
+ gluUnProject((double)(viewport[0]+viewport[2]/2),(double)(viewport[1]+viewport[3]/2),0,modelMatrix,projMatrix,viewport,&obj1x,&obj1y,&obj1z);
+ gluUnProject((double)(viewport[0]+viewport[2]/2),(double)(viewport[1]+viewport[3]/2),1,modelMatrix,projMatrix,viewport,&obj2x,&obj2y,&obj2z);
+
+ double d = (2 * PointDistance(obj1x, obj1y, obj1z, obj2x, obj2y, obj2z));
+ for (out = 0; out < 10000; out++) // Zoom in
+ {
+ eye[0] += (float)((obj2x-obj1x)/d);
+ eye[1] += (float)((obj2y-obj1y)/d);
+ eye[2] += (float)((obj2z-obj1z)/d);
+ target[0] += (float)((obj2x-obj1x)/d);
+ target[1] += (float)((obj2y-obj1y)/d);
+ target[2] += (float)((obj2z-obj1z)/d);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt(eye[0], eye[1], eye[2], target[0], target[1], target[2], up[0], up[1], up[2]);
+
+ glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
+
+ double px,py,pz;
+ gluUnProject((double)(viewport[0]+viewport[2]/2),(double)(viewport[1]+viewport[3]/2),0,modelMatrix,projMatrix,viewport,&px,&py,&pz);
+
+ double u1 = ((obj2x-obj1x)*(px-line[0][0])+(obj2y-obj1y)*(py-line[0][1])+(obj2z-obj1z)*(pz-line[0][2]))
+ /((obj2x-obj1x)*(px-line[1][0])+(obj2y-obj1y)*(py-line[1][1])+(obj2z-obj1z)*(pz-line[1][2]));
+ double u2 = ((obj2x-obj1x)*(px-line[2][0])+(obj2y-obj1y)*(py-line[2][1])+(obj2z-obj1z)*(pz-line[2][2]))
+ /((obj2x-obj1x)*(px-line[3][0])+(obj2y-obj1y)*(py-line[3][1])+(obj2z-obj1z)*(pz-line[3][2]));
+
+ double winx, winy, winz;
+ gluProject (line[0][0]+u1*(line[1][0]-line[0][0]), line[0][1]+u1*(line[1][1]-line[0][1]), line[0][2]+u1*(line[1][2]-line[0][2]), modelMatrix, projMatrix, viewport, &winx, &winy, &winz);
+ if ((winx < viewport[0] + 1) || (winy < viewport[1] + 1) ||
+ (winx > viewport[0] + viewport[2] - 1) || (winy > viewport[1] + viewport[3] - 1))
+ out = 10000;
+
+ gluProject (line[2][0]+u2*(line[3][0]-line[2][0]), line[2][1]+u2*(line[3][1]-line[2][1]), line[2][2]+u2*(line[3][2]-line[2][2]), modelMatrix, projMatrix, viewport, &winx, &winy, &winz);
+ if ((winx < viewport[0] + 1) || (winy < viewport[1] + 1) ||
+ (winx > viewport[0] + viewport[2] - 1) || (winy > viewport[1] + viewport[3] - 1))
+ out = 10000;
+ }
+ pCam->ChangeKey(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKey, eye, CK_EYE);
+ pCam->ChangeKey(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKey, target, CK_TARGET);
+ pCam->UpdateInformation(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation);
+// m_pViewModeless->UpdatePosition(GetActiveCamera());
+ InvalidateRect (NULL, FALSE);
+ }
+
+ if (m_ptTrack != pt)
+ {
+// if (m_Lights.GetCount() == 8)
+// m_nCurAction = ACTION_SELECT;
+// CheckPoint();
+ }
+ }
+*/
+}
+
+void Project::StartTracking(int mode)
+{
+ SystemCaptureMouse();
+ m_nTracking = mode;
+ m_pTrackFile = new File(true);
+ FileSave(m_pTrackFile, true);
+}
+
+void Project::SnapPoint(float *x, float *y, float *z)
+{
+ int i;
+
+ if (m_nSnap & LC_DRAW_SNAP_X)
+ {
+ i = (int)(*x/0.4f);
+ *x = 0.4f * i;
+ }
+
+ if (m_nSnap & LC_DRAW_SNAP_Y)
+ {
+ i = (int)(*y/0.4f);
+ *y = 0.4f * i;
+ }
+
+ if (m_nSnap & LC_DRAW_SNAP_Z)
+ {
+ i = (int)(*z/0.32f);
+ *z = 0.32f * i;
+ }
+}
+
+void Project::MoveSelectedObjects(float x, float y, float z)
+{
+ Piece* pPiece;
+ Camera* pCamera;
+ Light* pLight;
+
+ if (m_nSnap & LC_DRAW_LOCK_X)
+ x = 0;
+ if (m_nSnap & LC_DRAW_LOCK_Y)
+ y = 0;
+ if (m_nSnap & LC_DRAW_LOCK_Z)
+ z = 0;
+
+ for (pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ if (pCamera->IsSelected())
+ {
+ pCamera->Move(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, x, y, z);
+ pCamera->UpdatePosition(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation);
+ }
+
+ for (pLight = m_pLights; pLight; pLight = pLight->m_pNext)
+ if (pLight->IsSelected())
+ {
+// pLight->UpdatePosition(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation);
+ }
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ pPiece->Move(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, x, y, z);
+ pPiece->UpdatePosition(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation);
+ }
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ pPiece->CalculateConnections(m_pConnections, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, false, true);
+
+ // TODO: move group centers
+}
+
+void Project::RotateSelectedObjects(float x, float y, float z)
+{
+ if (m_nSnap & LC_DRAW_LOCK_X)
+ x = 0;
+ if (m_nSnap & LC_DRAW_LOCK_Y)
+ y = 0;
+ if (m_nSnap & LC_DRAW_LOCK_Z)
+ z = 0;
+
+ if (x == 0 && y == 0 && z == 0)
+ return;
+
+ float bs[6] = { 10000, 10000, 10000, -10000, -10000, -10000 };
+ float pos[3], rot[4];
+ int nSel = 0;
+ Piece* pPiece;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ pPiece->CompareBoundingBox (bs);
+ nSel++;
+ }
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ {
+ if (!pPiece->IsSelected())
+ continue;
+
+ pPiece->GetPosition(pos);
+ pPiece->GetRotation(rot);
+ Matrix m(rot, pos);
+
+ if (nSel == 1)
+ {
+ if (!(m_nSnap & LC_DRAW_LOCK_X))
+ m.Rotate(x,1,0,0);
+ if (!(m_nSnap & LC_DRAW_LOCK_Y))
+ m.Rotate(y,0,1,0);
+ if (!(m_nSnap & LC_DRAW_LOCK_Z))
+ m.Rotate(z,0,0,1);
+ }
+ else
+ {
+ if (!(m_nSnap & LC_DRAW_LOCK_X))
+ m.RotateCenter(x,1,0,0,(bs[0]+bs[3])/2,(bs[1]+bs[4])/2,(bs[2]+bs[5])/2);
+ if (!(m_nSnap & LC_DRAW_LOCK_Y))
+ m.RotateCenter(y,0,1,0,(bs[0]+bs[3])/2,(bs[1]+bs[4])/2,(bs[2]+bs[5])/2);
+ if (!(m_nSnap & LC_DRAW_LOCK_Z))
+ m.RotateCenter(z,0,0,1,(bs[0]+bs[3])/2,(bs[1]+bs[4])/2,(bs[2]+bs[5])/2);
+ m.GetTranslation(pos);
+
+ // TODO: check if moved
+ pPiece->ChangeKey(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, pos, PK_POSITION);
+ }
+
+ m.ToAxisAngle(rot);
+ pPiece->ChangeKey(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, rot, PK_ROTATION);
+ pPiece->UpdatePosition(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation);
+/*
+ for (POSITION pos2 = m_Pieces.GetHeadPosition(); pos2 != NULL;)
+ {
+ CPiece* tmp = m_Pieces.GetNext(pos2);
+ if (tmp == pPiece)
+ continue;
+
+ if (pPiece->Collide(tmp))
+ wprintf("Collision");
+ else
+ wprintf("No Collision");
+ }
+*/
+ }
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ pPiece->CalculateConnections(m_pConnections, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, false, true);
+}
+
+bool Project::OnKeyDown(char nKey, bool bControl, bool bShift)
+{
+ bool ret = false;
+
+ if (IsDrawing())
+ return false;
+
+ switch (nKey)
+ {
+ case KEY_ESCAPE:
+ {
+ if (m_nTracking != LC_TRACK_NONE)
+ StopTracking(false);
+ ret = true;
+ } break;
+
+ case KEY_INSERT:
+ {
+ HandleCommand(LC_PIECE_INSERT, 0);
+ ret = true;
+ } break;
+
+ case KEY_DELETE:
+ {
+ HandleCommand(LC_PIECE_DELETE, 0);
+ ret = true;
+ } break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ if (bControl)
+ {
+ m_nCurClipboard = nKey - 0x30;
+ SystemUpdatePaste(m_pClipboard[m_nCurClipboard] != NULL);
+ }
+ else
+ {
+ m_nMoveSnap = nKey - 0x30;
+ SystemUpdateMoveSnap(m_nMoveSnap);
+ }
+ ret = true;
+ } break;
+
+ case 'F':
+ {
+ HandleCommand(LC_VIEW_CAMERA_MENU, LC_CAMERA_FRONT);
+ ret = true;
+ } break;
+ case 'B':
+ {
+ HandleCommand(LC_VIEW_CAMERA_MENU, LC_CAMERA_BACK); break;
+ ret = true;
+ } break;
+ case 'T':
+ {
+ HandleCommand(LC_VIEW_CAMERA_MENU, LC_CAMERA_TOP); break;
+ ret = true;
+ } break;
+ case 'U':
+ {
+ HandleCommand(LC_VIEW_CAMERA_MENU, LC_CAMERA_UNDER); break;
+ ret = true;
+ } break;
+ case 'L':
+ {
+ HandleCommand(LC_VIEW_CAMERA_MENU, LC_CAMERA_LEFT); break;
+ ret = true;
+ } break;
+ case 'R':
+ {
+ HandleCommand(LC_VIEW_CAMERA_MENU, LC_CAMERA_RIGHT); break;
+ ret = true;
+ } break;
+ case 'M':
+ {
+ HandleCommand(LC_VIEW_CAMERA_MENU, LC_CAMERA_MAIN); break;
+ ret = true;
+ } break;
+
+ case KEY_PLUS: // case '+': case '=':
+ {
+ HandleCommand(LC_VIEW_ZOOMIN, 0);
+ ret = true;
+ } break;
+
+ case KEY_MINUS: // case '-': case '_':
+ {
+ HandleCommand(LC_VIEW_ZOOMOUT, 0);
+ ret = true;
+ } break;
+
+ case KEY_TAB:
+ {
+ if (m_pPieces == NULL)
+ break;
+
+ Piece* pFocus = NULL, *pPiece;
+ for (pFocus = m_pPieces; pFocus; pFocus = pFocus->m_pNext)
+ if (pFocus->IsFocused())
+ break;
+
+ SelectAndFocusNone(false);
+
+ if (pFocus == NULL)
+ {
+ if (bShift)
+ {
+ // Focus the last visible piece.
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation))
+ pFocus = pPiece;
+ }
+ else
+ {
+ // Focus the first visible piece.
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation))
+ {
+ pFocus = pPiece;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (bShift)
+ {
+ // Focus the previous visible piece.
+ Piece* pBest;
+ pPiece = pFocus;
+ for (;;)
+ {
+ if (pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation))
+ pBest = pPiece;
+
+ if (pPiece->m_pNext != NULL)
+ {
+ if (pPiece->m_pNext == pFocus)
+ break;
+ else
+ pPiece = pPiece->m_pNext;
+ }
+ else
+ {
+ if (pFocus == m_pPieces)
+ break;
+ else
+ pPiece = m_pPieces;
+ }
+ }
+
+ pFocus = pBest;
+ }
+ else
+ {
+ // Focus the next visible piece.
+ pPiece = pFocus;
+ for (;;)
+ {
+ if (pPiece != pFocus)
+ if (pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation))
+ {
+ pFocus = pPiece;
+ break;
+ }
+
+ if (pPiece->m_pNext != NULL)
+ {
+ if (pPiece->m_pNext == pFocus)
+ break;
+ else
+ pPiece = pPiece->m_pNext;
+ }
+ else
+ {
+ if (pFocus == m_pPieces)
+ break;
+ else
+ pPiece = m_pPieces;
+ }
+ }
+ }
+ }
+
+ if (pFocus != NULL)
+ {
+ pFocus->Focus();
+ Group* pGroup = pFocus->GetTopGroup();
+ if (pGroup != NULL)
+ {
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsVisible(m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation) &&
+ pPiece->GetTopGroup() == pGroup)
+ pPiece->Select();
+ }
+ }
+
+ UpdateSelection();
+ SystemRedrawView();
+ SystemUpdateFocus(pFocus, LC_PIECE|LC_UPDATE_OBJECT|LC_UPDATE_TYPE);
+ ret = true;
+ } break;
+
+ case KEY_UP: case KEY_DOWN: case KEY_LEFT:
+ case KEY_RIGHT: case KEY_NEXT: case KEY_PRIOR:
+// if (AnyObjectSelected(FALSE))
+ {
+ float axis[3];
+ if (bShift)
+ {
+ if (m_nSnap & LC_DRAW_SNAP_A)
+ axis[0] = axis[1] = axis[2] = m_nAngleSnap;
+ else
+ axis[0] = axis[1] = axis[2] = 1;
+ }
+ else
+ {
+ if (m_nMoveSnap > 0)
+ {
+ axis[0] = axis[1] = 0.8f * m_nMoveSnap;
+ axis[2] = 0.32f * m_nMoveSnap;
+ }
+ else
+ {
+ axis[0] = axis[1] = 0.4f;
+ axis[2] = 0.32f;
+ }
+
+ if ((m_nSnap & LC_DRAW_SNAP_X == 0) || bControl)
+ axis[0] = 0.01f;
+ if ((m_nSnap & LC_DRAW_SNAP_Y == 0) || bControl)
+ axis[1] = 0.01f;
+ if ((m_nSnap & LC_DRAW_SNAP_Z == 0) || bControl)
+ axis[2] = 0.01f;
+ }
+
+ if (m_nSnap & LC_DRAW_MOVEAXIS)
+ {
+ switch (nKey)
+ {
+ case KEY_UP: {
+ axis[1] = axis[2] = 0; axis[0] = -axis[0];
+ } break;
+ case KEY_DOWN: {
+ axis[1] = axis[2] = 0;
+ } break;
+ case KEY_LEFT: {
+ axis[0] = axis[2] = 0; axis[1] = -axis[1];
+ } break;
+ case KEY_RIGHT: {
+ axis[0] = axis[2] = 0;
+ } break;
+ case KEY_NEXT: {
+ axis[0] = axis[1] = 0; axis[2] = -axis[2];
+ } break;
+ case KEY_PRIOR: {
+ axis[0] = axis[1] = 0;
+ } break;
+ }
+ }
+ else
+ {
+ // TODO: rewrite this
+
+ switch (nKey)
+ {
+ case KEY_UP: {
+ axis[1] = axis[2] = 0; axis[0] = -axis[0];
+ } break;
+ case KEY_DOWN: {
+ axis[1] = axis[2] = 0;
+ } break;
+ case KEY_LEFT: {
+ axis[0] = axis[2] = 0; axis[1] = -axis[1];
+ } break;
+ case KEY_RIGHT: {
+ axis[0] = axis[2] = 0;
+ } break;
+ case KEY_NEXT: {
+ axis[0] = axis[1] = 0; axis[2] = -axis[2];
+ } break;
+ case KEY_PRIOR: {
+ axis[0] = axis[1] = 0;
+ } break;
+ }
+
+ double modelMatrix[16], projMatrix[16], p1[3], p2[3], p3[3];
+ float ax, ay;
+ int viewport[4];
+
+ glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
+ glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ gluUnProject( 5, 5, 0.1, modelMatrix,projMatrix,viewport,&p1[0],&p1[1],&p1[2]);
+ gluUnProject(10, 5, 0.1, modelMatrix,projMatrix,viewport,&p2[0],&p2[1],&p2[2]);
+ gluUnProject( 5,10, 0.1, modelMatrix,projMatrix,viewport,&p3[0],&p3[1],&p3[2]);
+
+ Vector vx((float)(p2[0] - p1[0]), (float)(p2[1] - p1[1]), 0);//p2[2] - p1[2] };
+ Vector x(1, 0, 0);
+ ax = vx.Angle(x);
+
+ Vector vy((float)(p3[0] - p1[0]), (float)(p3[1] - p1[1]), 0);//p2[2] - p1[2] };
+ Vector y(0, -1, 0);
+ ay = vy.Angle(y);
+
+ if (ax > 135)
+ axis[0] = -axis[0];
+
+ if (ay < 45)
+ axis[1] = -axis[1];
+
+ if (ax >= 45 && ax <= 135)
+ {
+ float tmp = axis[0];
+
+ ax = vx.Angle(y);
+ if (ax > 90)
+ {
+ axis[0] = -axis[1];
+ axis[1] = tmp;
+ }
+ else
+ {
+ axis[0] = axis[1];
+ axis[1] = -tmp;
+ }
+ }
+ }
+
+ if (bShift)
+ RotateSelectedObjects(axis[0], axis[1], axis[2]);
+ else
+ MoveSelectedObjects(axis[0], axis[1], axis[2]);
+ SystemRedrawView();
+ SetModifiedFlag(true);
+ CheckPoint((bShift) ? (char*) "Rotating" : (char*) "Moving");
+ SystemUpdateFocus(NULL, 0);
+ ret = true;
+ } break;
+ }
+
+ return ret;
+}
+
+void Project::OnLeftButtonDown(int x, int y)
+{
+ double modelMatrix[16], projMatrix[16], point[3];
+ int viewport[4];
+
+ if (IsDrawing())
+ return;
+
+ if (m_nTracking != LC_TRACK_NONE)
+ if (StopTracking(false))
+ return;
+
+ if (SetActiveViewport(x, y))
+ return;
+
+ m_bTrackCancel = false;
+ m_nDownX = x;
+ m_nDownY = y;
+
+ LoadViewportProjection();
+ glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
+ glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
+ glGetIntegerv(GL_VIEWPORT, viewport);
+
+ gluUnProject(x, y, 0.9, modelMatrix, projMatrix, viewport, &point[0], &point[1], &point[2]);
+ m_fTrack[0] = (float)point[0]; m_fTrack[1] = (float)point[1]; m_fTrack[2] = (float)point[2];
+
+ switch (m_nCurAction)
+ {
+ case LC_ACTION_SELECT:
+ case LC_ACTION_ERASER:
+ case LC_ACTION_PAINT:
+ {
+ BoundingBox* pBox;
+ pBox = FindObjectFromPoint(x, y);
+
+ if (m_nCurAction == LC_ACTION_SELECT)
+ {
+ SelectAndFocusNone(IsKeyDown(KEY_CONTROL));
+
+ if (pBox != NULL)
+ switch (pBox->GetOwnerType())
+ {
+ case LC_PIECE:
+ {
+ Piece* pPiece = (Piece*)pBox->GetOwner();
+ pPiece->Focus();
+ Group* pGroup = pPiece->GetTopGroup();
+
+ if (pGroup != NULL)
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->GetTopGroup() == pGroup)
+ pPiece->Select();
+ } break;
+
+ case LC_CAMERA:
+ {
+ ((Camera*)pBox->GetOwner())->FocusEye();
+ } break;
+
+ case LC_CAMERA_TARGET:
+ {
+ ((Camera*)pBox->GetOwner())->FocusTarget();
+ } break;
+
+ case LC_LIGHT:
+ {
+ ((Light*)pBox->GetOwner())->FocusEye();
+ } break;
+
+ case LC_LIGHT_TARGET:
+ {
+ ((Light*)pBox->GetOwner())->FocusTarget();
+ } break;
+ }
+
+ UpdateSelection();
+ SystemRedrawView();
+ if (pBox)
+ SystemUpdateFocus(pBox->GetOwner(), pBox->GetOwnerType()|LC_UPDATE_OBJECT|LC_UPDATE_TYPE);
+ else
+ SystemUpdateFocus(NULL, LC_UPDATE_OBJECT);
+ }
+
+ if ((m_nCurAction == LC_ACTION_ERASER) && (pBox != NULL))
+ {
+ switch (pBox->GetOwnerType())
+ {
+ case LC_PIECE:
+ {
+ Piece* pPiece = (Piece*)pBox->GetOwner();
+ RemovePiece(pPiece);
+ delete pPiece;
+// CalculateStep();
+ RemoveEmptyGroups();
+ } break;
+
+ case LC_CAMERA:
+ case LC_CAMERA_TARGET:
+ {
+ Camera* pCamera = (Camera*)pBox->GetOwner();
+ bool bCanDelete = pCamera->IsUser();
+
+ for (int i = 0; i < 4; i++)
+ if (pCamera == m_pViewCameras[i])
+ bCanDelete = false;
+
+ if (bCanDelete)
+ {
+ Camera* pPrev;
+ for (pPrev = m_pCameras; pPrev; pPrev = pPrev->m_pNext)
+ if (pPrev->m_pNext == pCamera)
+ {
+ pPrev->m_pNext = pCamera->m_pNext;
+ delete pCamera;
+ SystemUpdateCameraMenu(m_pCameras);
+ SystemUpdateCurrentCamera(NULL, m_pViewCameras[m_nActiveViewport], m_pCameras);
+ break;
+ }
+ }
+ } break;
+
+ case LC_LIGHT:
+ case LC_LIGHT_TARGET:
+ {
+/* pos = m_Lights.Find(pObject->m_pParent);
+ m_Lights.RemoveAt(pos);
+ delete pObject->m_pParent;
+*/ } break;
+ }
+
+ UpdateSelection();
+ SystemRedrawView();
+ SetModifiedFlag(true);
+ CheckPoint("Deleting");
+// AfxGetMainWnd()->PostMessage(WM_LC_UPDATE_INFO, NULL, OT_PIECE);
+ }
+
+ if ((m_nCurAction == LC_ACTION_PAINT) && (pBox != NULL) &&
+ (pBox->GetOwnerType() == LC_PIECE))
+ {
+ Piece* pPiece = (Piece*)pBox->GetOwner();
+
+ if (pPiece->GetColor() != m_nCurColor)
+ {
+ bool bTrans = pPiece->IsTransparent();
+ pPiece->SetColor(m_nCurColor);
+ if (bTrans != pPiece->IsTransparent())
+ pPiece->CalculateConnections(m_pConnections, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, true, true);
+
+ SetModifiedFlag(true);
+ CheckPoint("Painting");
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+ }
+ }
+ } break;
+
+ case LC_ACTION_INSERT:
+// case LC_ACTION_LIGHT:
+ {
+ if (m_nCurAction == LC_ACTION_INSERT)
+ {
+ Piece* pPiece = new Piece(m_pCurPiece);
+ SnapPoint(&m_fTrack[0], &m_fTrack[1], &m_fTrack[2]);
+ pPiece->Initialize(m_fTrack[0], m_fTrack[1], m_fTrack[2], m_nCurStep, m_nCurFrame, m_nCurColor);
+
+ SelectAndFocusNone(false);
+ pPiece->CreateName(m_pPieces);
+ AddPiece(pPiece);
+ pPiece->CalculateConnections(m_pConnections, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, false, true);
+ SystemUpdateFocus(pPiece, LC_PIECE|LC_UPDATE_OBJECT|LC_UPDATE_TYPE);
+ pPiece->Focus();
+ UpdateSelection();
+ SystemPieceComboAdd(m_pCurPiece->m_strDescription);
+
+ if (m_nSnap & LC_DRAW_MOVE)
+ SetAction(LC_ACTION_MOVE);
+ }
+/*
+ if (m_nCurAction == ACTION_LIGHT)
+ {
+ SelectAndFocusNone(FALSE);
+
+ CLight* pLight = new CLight(m_fTrackPos[0], m_fTrackPos[1], m_fTrackPos[2]);
+ m_Lights.AddTail(pLight);
+
+// if (m_Lights.GetCount() == 8)
+// m_nCurAction = ACTION_SELECT;
+// strcpy (m_Name
+// pFrame->UpdateInfo();
+ }
+*/
+// AfxGetMainWnd()->PostMessage(WM_LC_UPDATE_INFO, (WPARAM)pNew, OT_PIECE);
+ UpdateSelection();
+ SystemRedrawView();
+ SetModifiedFlag(true);
+ CheckPoint("Inserting");
+ } break;
+
+ case LC_ACTION_CAMERA:
+ {
+ double tmp[3];
+ gluUnProject(x+1, y-1, 0.9, modelMatrix, projMatrix, viewport, &tmp[0], &tmp[1], &tmp[2]);
+ SelectAndFocusNone(false);
+ StartTracking(LC_TRACK_START_LEFT);
+ Camera* pCamera = new Camera(m_fTrack[0], m_fTrack[1], m_fTrack[2], (float)tmp[0], (float)tmp[1], (float)tmp[2], m_pCameras);
+ pCamera->FocusTarget();
+ UpdateSelection();
+ SystemRedrawView();
+ SystemUpdateFocus(pCamera, LC_CAMERA|LC_UPDATE_OBJECT|LC_UPDATE_TYPE);
+ } break;
+
+ case LC_ACTION_MOVE:
+ {
+ bool sel = false;
+
+ for (Piece* pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ sel = true;
+ break;
+ }
+
+ if (!sel)
+ for (Camera* pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ if (pCamera->IsSelected())
+ {
+ sel = true;
+ break;
+ }
+
+ if (!sel)
+ for (Light* pLight = m_pLights; pLight; pLight = pLight->m_pNext)
+ if (pLight->IsSelected())
+ {
+ sel = true;
+ break;
+ }
+
+ if (sel)
+ StartTracking(LC_TRACK_START_LEFT);
+ } break;
+
+ case LC_ACTION_ROTATE:
+ {
+ Piece* pPiece;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ StartTracking(LC_TRACK_START_LEFT);
+ break;
+ }
+ } break;
+
+ case LC_ACTION_ZOOM:
+ case LC_ACTION_ROLL:
+ case LC_ACTION_PAN:
+ case LC_ACTION_ROTATE_VIEW:
+ {
+ StartTracking(LC_TRACK_START_LEFT);
+ } break;
+
+ case LC_ACTION_ZOOM_REGION:
+ {
+ SystemCaptureMouse();
+ m_nTracking = LC_TRACK_START_LEFT;
+ m_pTrackFile = NULL;
+ } break;
+
+ case LC_ACTION_SPOTLIGHT:
+ break;
+ }
+}
+
+void Project::OnLeftButtonDoubleClick(int x, int y)
+{
+ double modelMatrix[16], projMatrix[16], point[3];
+ int viewport[4];
+
+ if (IsDrawing())
+ return;
+
+ if (SetActiveViewport(x, y))
+ return;
+
+ LoadViewportProjection();
+ glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
+ glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
+ glGetIntegerv(GL_VIEWPORT, viewport);
+
+ gluUnProject(x, y, 0.9, modelMatrix, projMatrix, viewport, &point[0], &point[1], &point[2]);
+ m_fTrack[0] = (float)point[0]; m_fTrack[1] = (float)point[1]; m_fTrack[2] = (float)point[2];
+
+ BoundingBox* pBox;
+ pBox = FindObjectFromPoint(x, y);
+
+// if (m_nCurAction == LC_ACTION_SELECT)
+ {
+ SelectAndFocusNone(IsKeyDown(KEY_CONTROL));
+
+ if (pBox != NULL)
+ switch (pBox->GetOwnerType())
+ {
+ case LC_PIECE:
+ {
+ Piece* pPiece = (Piece*)pBox->GetOwner();
+ pPiece->Focus();
+ Group* pGroup = pPiece->GetTopGroup();
+
+ if (pGroup != NULL)
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->GetTopGroup() == pGroup)
+ pPiece->Select();
+ } break;
+
+ case LC_CAMERA:
+ {
+ ((Camera*)pBox->GetOwner())->FocusEye();
+ } break;
+
+ case LC_CAMERA_TARGET:
+ {
+ ((Camera*)pBox->GetOwner())->FocusTarget();
+ } break;
+
+ case LC_LIGHT:
+ {
+ ((Light*)pBox->GetOwner())->FocusEye();
+ } break;
+
+ case LC_LIGHT_TARGET:
+ {
+ ((Light*)pBox->GetOwner())->FocusTarget();
+ } break;
+ }
+
+ UpdateSelection();
+ SystemRedrawView();
+ if (pBox)
+ SystemUpdateFocus(pBox->GetOwner(), pBox->GetOwnerType()|LC_UPDATE_OBJECT|LC_UPDATE_TYPE);
+ else
+ SystemUpdateFocus(NULL, LC_UPDATE_OBJECT);
+ }
+}
+
+void Project::OnLeftButtonUp(int x, int y)
+{
+ StopTracking(true);
+}
+
+void Project::OnRightButtonDown(int x, int y)
+{
+ double modelMatrix[16], projMatrix[16], point[3];
+ int viewport[4];
+
+ if (StopTracking(false))
+ return;
+ if (SetActiveViewport(x, y))
+ return;
+
+ m_nDownX = x;
+ m_nDownY = y;
+ m_bTrackCancel = false;
+
+ LoadViewportProjection();
+ glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
+ glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
+ glGetIntegerv(GL_VIEWPORT, viewport);
+
+ gluUnProject(x, y, 0.9, modelMatrix, projMatrix, viewport, &point[0], &point[1], &point[2]);
+ m_fTrack[0] = (float)point[0]; m_fTrack[1] = (float)point[1]; m_fTrack[2] = (float)point[2];
+
+ switch (m_nCurAction)
+ {
+ case LC_ACTION_MOVE:
+ {
+ bool sel = false;
+
+ for (Piece* pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ sel = true;
+ break;
+ }
+
+ if (!sel)
+ for (Camera* pCamera = m_pCameras; pCamera; pCamera = pCamera->m_pNext)
+ if (pCamera->IsSelected())
+ {
+ sel = true;
+ break;
+ }
+
+ if (!sel)
+ for (Light* pLight = m_pLights; pLight; pLight = pLight->m_pNext)
+ if (pLight->IsSelected())
+ {
+ sel = true;
+ break;
+ }
+
+ if (sel)
+ StartTracking(LC_TRACK_START_RIGHT);
+ } break;
+
+ case LC_ACTION_ROTATE:
+ {
+ Piece* pPiece;
+
+ for (pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ {
+ StartTracking(LC_TRACK_START_RIGHT);
+ break;
+ }
+ } break;
+ }
+}
+
+void Project::OnRightButtonUp(int x, int y)
+{
+ if (!StopTracking(true) && !m_bTrackCancel)
+ SystemDoPopupMenu(1, -1, -1);
+ m_bTrackCancel = false;
+}
+
+void Project::OnMouseMove(int x, int y)
+{
+ // && m_nAction != ACTION_INSERT
+ if (m_nTracking == LC_TRACK_NONE)
+ return;
+
+ if (m_nTracking == LC_TRACK_START_RIGHT)
+ m_nTracking = LC_TRACK_RIGHT;
+
+ if (m_nTracking == LC_TRACK_START_LEFT)
+ m_nTracking = LC_TRACK_LEFT;
+
+ if (IsDrawing())
+ return;
+
+ double modelMatrix[16], projMatrix[16], tmp[3];
+ int viewport[4];
+ float ptx, pty, ptz;
+
+ LoadViewportProjection();
+ glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
+ glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
+ glGetIntegerv(GL_VIEWPORT, viewport);
+
+ gluUnProject(x, y, 0.9, modelMatrix, projMatrix, viewport, &tmp[0], &tmp[1], &tmp[2]);
+ ptx = (float)tmp[0]; pty = (float)tmp[1]; ptz = (float)tmp[2];
+
+ switch (m_nCurAction)
+ {
+ case LC_ACTION_INSERT:
+ // TODO: handle action_insert (draw preview)
+ break;
+
+ case LC_ACTION_SPOTLIGHT:
+ break;
+
+ case LC_ACTION_CAMERA:
+ {
+ float mouse = 10.0f/(21 - m_nMouse);
+ float delta[3] = { (ptx - m_fTrack[0])*mouse,
+ (pty - m_fTrack[1])*mouse, (ptz - m_fTrack[2])*mouse };
+
+ m_fTrack[0] = ptx;
+ m_fTrack[1] = pty;
+ m_fTrack[2] = ptz;
+
+ Camera* pCamera = m_pCameras;
+ while (pCamera->m_pNext != NULL)
+ pCamera = pCamera->m_pNext;
+
+ float target[3];
+ pCamera->GetTarget(target);
+ target[0] += delta[0];
+ target[1] += delta[1];
+ target[2] += delta[2];
+ pCamera->ChangeKey(1, m_bAnimation, false, target, CK_TARGET);
+ pCamera->UpdatePosition(1, m_bAnimation);
+
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+ } break;
+
+ case LC_ACTION_MOVE:
+ {
+ // TODO: rewrite
+
+ float mouse = 10.0f/(21-m_nMouse);
+ float delta[3] = {
+ (ptx - m_fTrack[0])*mouse,
+ (pty - m_fTrack[1])*mouse,
+ (ptz - m_fTrack[2])*mouse };
+ float d[3] = { delta[0], delta[1], delta[2] };
+
+ SnapPoint(&delta[0], &delta[1], &delta[2]);
+
+ m_fTrack[0] = ptx + (delta[0]-d[0])/mouse;
+ m_fTrack[1] = pty + (delta[1]-d[1])/mouse;
+ m_fTrack[2] = ptz + (delta[2]-d[2])/mouse;
+
+ if (m_nSnap & LC_DRAW_3DMOUSE)
+ MoveSelectedObjects(delta[0], delta[1], delta[2]);
+ else
+ {
+ if (m_nTracking == LC_TRACK_LEFT)
+ MoveSelectedObjects(delta[0], delta[1], 0);
+ else
+ MoveSelectedObjects(0, 0, delta[2]);
+ }
+
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+ } break;
+
+ case LC_ACTION_ROTATE:
+ {
+ // TODO: rewrite
+
+ float mouse = 360.0f/(21-m_nMouse);
+ float delta[3] = {
+ (ptx - m_fTrack[0])*mouse,
+ (pty - m_fTrack[1])*mouse,
+ (ptz - m_fTrack[2])*mouse };
+ float d[3] = { delta[0], delta[1], delta[2] };
+
+ ldiv_t result;
+ for (int i = 0; i < 3; i++)
+ if (m_nSnap & LC_DRAW_SNAP_A)
+ {
+ result = ldiv ((long)delta[i], m_nAngleSnap);
+ delta[i] = (float)(result.quot * m_nAngleSnap);
+ }
+ else
+ {
+ result = ldiv ((long)delta[i], 1);
+ delta[i] = (float)result.quot;
+ }
+
+ m_fTrack[0] = ptx + (delta[0]-d[0])/mouse;
+ m_fTrack[1] = pty + (delta[1]-d[1])/mouse;
+ m_fTrack[2] = ptz + (delta[2]-d[2])/mouse;
+
+ RotateSelectedObjects(delta[0], delta[1], delta[2]);
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+ } break;
+
+ case LC_ACTION_ZOOM:
+ {
+ if (m_nDownY == y)
+ break;
+
+ m_pViewCameras[m_nActiveViewport]->DoZoom(y - m_nDownY, m_nMouse, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys);
+ m_nDownY = y;
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+ } break;
+
+ case LC_ACTION_ZOOM_REGION:
+ {
+ Render(false);
+
+ glColor3f (0,0,0);
+ glViewport(0, 0, m_nViewX, m_nViewY);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, m_nViewX, 0, m_nViewY, -1, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ int vx = (int)(viewports[m_nViewportMode].dim[m_nActiveViewport][0] * ((float)m_nViewX));
+ int vy = (int)(viewports[m_nViewportMode].dim[m_nActiveViewport][1] * ((float)m_nViewY));
+ int vw = (int)(viewports[m_nViewportMode].dim[m_nActiveViewport][2] * ((float)m_nViewX));
+ int vh = (int)(viewports[m_nViewportMode].dim[m_nActiveViewport][3] * ((float)m_nViewY));
+
+ int rx, ry;
+ rx = x;
+ ry = y;
+
+ if (rx < vx) rx = vx;
+ if (ry < vy) ry = vy;
+ if (rx > vx+vw) rx = vx+vw;
+ if (ry > vy+vh) ry = vy+vh;
+
+ glBegin (GL_LINE_LOOP);
+ glVertex2i(rx, ry);
+ glVertex2i(m_nDownX, ry);
+ glVertex2i(m_nDownX, m_nDownY);
+ glVertex2i(rx, m_nDownY);
+ glEnd();
+
+ SystemSwapBuffers();
+ } break;
+
+ case LC_ACTION_PAN:
+ {
+ if ((m_nDownY == y) && (m_nDownX == x))
+ break;
+
+ m_pViewCameras[m_nActiveViewport]->DoPan(x - m_nDownX, y - m_nDownY, m_nMouse, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys);
+ m_nDownX = x;
+ m_nDownY = y;
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+ } break;
+
+ case LC_ACTION_ROTATE_VIEW:
+ {
+ if ((m_nDownY == y) && (m_nDownX == x))
+ break;
+
+ // We can't rotate the side cameras.
+ if (m_pViewCameras[m_nActiveViewport]->IsSide())
+ {
+ float eye[3], target[3], up[3];
+ m_pViewCameras[m_nActiveViewport]->GetEye(eye);
+ m_pViewCameras[m_nActiveViewport]->GetTarget(target);
+ m_pViewCameras[m_nActiveViewport]->GetUp(up);
+ Camera* pCamera = new Camera(eye, target, up, m_pCameras);
+
+ m_pViewCameras[m_nActiveViewport] = pCamera;
+ SystemUpdateCameraMenu(m_pCameras);
+ SystemUpdateCurrentCamera(NULL, pCamera, m_pCameras);
+ }
+
+
+ float bs[6] = { 10000, 10000, 10000, -10000, -10000, -10000 };
+ for (Piece* pPiece = m_pPieces; pPiece; pPiece = pPiece->m_pNext)
+ if (pPiece->IsSelected())
+ pPiece->CompareBoundingBox(bs);
+ bs[0] = (bs[0]+bs[3])/2;
+ bs[1] = (bs[1]+bs[4])/2;
+ bs[2] = (bs[2]+bs[5])/2;
+
+ m_pViewCameras[m_nActiveViewport]->DoRotate(x - m_nDownX, y - m_nDownY, m_nMouse, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys, bs);
+ m_nDownX = x;
+ m_nDownY = y;
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+ } break;
+
+ case LC_ACTION_ROLL:
+ {
+ if (m_nDownX == x)
+ break;
+
+ m_pViewCameras[m_nActiveViewport]->DoRoll(x - m_nDownX, m_nMouse, m_bAnimation ? m_nCurFrame : m_nCurStep, m_bAnimation, m_bAddKeys);
+ m_nDownX = x;
+ SystemUpdateFocus(NULL, 0);
+ SystemRedrawView();
+ } break;
+ }
+}
diff --git a/common/project.h b/common/project.h
new file mode 100644
index 0000000..c00f802
--- /dev/null
+++ b/common/project.h
@@ -0,0 +1,255 @@
+#ifndef _PROJECT_H_
+#define _PROJECT_H_
+
+#include "defines.h"
+#include "typedefs.h"
+
+typedef enum
+{
+ LC_TRACK_NONE, LC_TRACK_START_LEFT, LC_TRACK_LEFT,
+ LC_TRACK_START_RIGHT, LC_TRACK_RIGHT
+} LC_MOUSE_TRACK;
+
+class Piece;
+class Camera;
+class Light;
+class Group;
+class Texture;
+class BoundingBox;
+class Terrain;
+class PieceInfo;
+class Matrix;
+
+// Undo support
+
+#include "file.h"
+
+typedef struct UNDOINFO
+{
+ File file;
+ char strText[21];
+ UNDOINFO* pNext;
+ UNDOINFO() : file(true) { pNext = NULL; };
+} UNDOINFO;
+
+class Project
+{
+public:
+// Constructors
+ Project();
+ ~Project();
+ bool Initialize(int argc, char *argv[], char* libpath);
+
+// Attributes
+public:
+ bool IsModified()
+ { return m_bModified; }
+ void SetModifiedFlag(bool bModified)
+ { m_bModified = bModified; }
+
+ // Access to protected members
+ unsigned char GetLastStep();
+ bool IsAnimation()
+ { return m_bAnimation; }
+ int GetPieceLibraryCount()
+ { return m_nPieceCount; }
+ const char* GetLibraryPath()
+ { return m_LibraryPath; }
+ void SetCurrentPiece(PieceInfo* pInfo)
+ { m_pCurPiece = pInfo; }
+ int GetCurrentColor()
+ { return m_nCurColor; }
+ float* GetBackgroundColor()
+ { return m_fBackground; }
+ Camera* GetCamera(int i);
+ PieceInfo* GetPieceInfo(int index);
+ void GetTimeRange(int* from, int* to)
+ {
+ *from = m_bAnimation ? m_nCurFrame : m_nCurStep;
+ *to = m_bAnimation ? m_nTotalFrames : 255;
+ }
+
+ void GetArrays(Piece** ppPiece, Camera** ppCamera, Light** ppLight)
+ {
+ *ppPiece = m_pPieces;
+ *ppCamera = m_pCameras;
+ *ppLight = m_pLights;
+ }
+
+ void SetPathName(char* lpszPathName, bool bAddToMRU);
+ void SetTitle(char* lpszTitle);
+
+public:
+ // Special notifications
+ void DeleteContents(bool bUndo); // delete doc items etc
+ void LoadDefaults(bool cameras);
+
+ void Render(bool bToMemory);
+ void SetViewSize(int cx, int cy);
+ Texture* FindTexture(char* name);
+ PieceInfo* FindPieceInfo(char* name);
+ void CheckAutoSave();
+ void GetFocusPosition(float* pos);
+
+// Implementation
+protected:
+ // default implementation
+ char m_strTitle[LC_MAXPATH];
+ char m_strPathName[LC_MAXPATH];
+ bool m_bModified; // changed since last saved
+ char m_strRecentFiles[4][LC_MAXPATH];
+
+ char m_strAuthor[101];
+ char m_strDescription[101];
+ char m_strComments[256];
+
+ // Piece library
+ bool LoadPieceLibrary();
+ char* m_LibraryPath; // path to the library files
+ int m_nPieceCount; // number of pieces
+ PieceInfo* m_pPieceIdx; // index
+ int m_nTextureCount;
+ Texture* m_pTextures;
+ char* m_pMovedReference;
+ int m_nMovedCount;
+
+ // Undo support
+ UNDOINFO* m_pUndoList;
+ UNDOINFO* m_pRedoList;
+ bool m_bUndoOriginal;
+ void CheckPoint(char* text);
+
+ // Objects
+ Piece* m_pPieces;
+ Camera* m_pCameras;
+ Light* m_pLights;
+ Group* m_pGroups;
+ Camera* m_pViewCameras[4];
+ Terrain* m_pTerrain;
+ File* m_pClipboard[10];
+ unsigned char m_nCurClipboard;
+
+ CONNECTION_TYPE m_pConnections[LC_CONNECTIONS];
+
+ void AddPiece(Piece* pPiece);
+ void RemovePiece(Piece* pPiece);
+ bool RemoveSelectedObjects();
+ BoundingBox* FindObjectFromPoint(int x, int y);
+ void SelectAndFocusNone(bool bFocusOnly);
+ void CalculateStep();
+ void MoveSelectedObjects(float x, float y, float z);
+ void RotateSelectedObjects(float x, float y, float z);
+ void SnapPoint(float *x, float *y, float *z);
+
+ // Rendering
+ void RenderScene(bool bShaded, bool bDrawViewports);
+ void RenderViewports(bool bBackground, bool bLines);
+ void RenderBoxes(bool bHilite);
+ void RenderInitialize();
+ void CreateImages(LC_IMAGE** images, int width, int height, unsigned short from, unsigned short to, bool hilite);
+ void CreateHTMLPieceList(FILE* f, int nStep, bool bImages, char* ext);
+
+ inline bool IsDrawing()
+ {
+ if (m_bRendering)
+ m_bStopRender = true;
+ return m_bRendering;
+ }
+
+ bool m_bRendering;
+ bool m_bStopRender;
+ File* m_pTrackFile;
+ bool m_bTrackCancel;
+ int m_nTracking;
+ int m_nDownX;
+ int m_nDownY;
+ float m_fTrack[3];
+ int m_nMouse;
+
+ void LoadViewportProjection();
+ bool SetActiveViewport(int x, int y);
+ bool StopTracking(bool bAccept);
+ void StartTracking(int mode);
+ void UpdateSelection();
+ void RemoveEmptyGroups();
+
+public:
+ // Call this functions from each OS
+ void OnLeftButtonDown(int x, int y);
+ void OnLeftButtonUp(int x, int y);
+ void OnLeftButtonDoubleClick(int x, int y);
+ void OnRightButtonDown(int x, int y);
+ void OnRightButtonUp(int x, int y);
+ void OnMouseMove(int x, int y);
+ bool OnKeyDown(char nKey, bool bControl, bool bShift);
+
+ void SetAction(int nAction);
+ void HandleNotify(LC_NOTIFY id, unsigned long param);
+ void HandleCommand(LC_COMMANDS id, unsigned long nParam);
+
+protected:
+ // State variables
+ unsigned char m_nViewportMode;
+ unsigned char m_nActiveViewport;
+ int m_nViewX;
+ int m_nViewY;
+ PieceInfo* m_pCurPiece;
+ unsigned char m_nCurColor;
+ unsigned char m_nCurAction;
+ unsigned char m_nCurGroup;
+ bool m_bAnimation;
+ bool m_bAddKeys;
+ unsigned char m_nFPS;
+ unsigned char m_nCurStep;
+ unsigned short m_nCurFrame;
+ unsigned short m_nTotalFrames;
+
+ unsigned long m_nScene;
+ unsigned long m_nDetail;
+ unsigned long m_nSnap;
+ unsigned short m_nMoveSnap;
+ unsigned short m_nAngleSnap;
+ unsigned short m_nGridSize;
+ float m_fLineWidth;
+ float m_fFogDensity;
+ float m_fFogColor[4];
+ float m_fAmbient[4];
+ float m_fBackground[4];
+ float m_fGradient1[3];
+ float m_fGradient2[3];
+ char m_strFooter[256];
+ char m_strHeader[256];
+
+ GLuint m_nGridList;
+ unsigned long m_nAutosave;
+ unsigned long m_nSaveTimer;
+ char m_strModelsPath[LC_MAXPATH];
+ char m_strBackground[LC_MAXPATH];
+ Texture* m_pBackground;
+
+protected:
+ // implementation helpers
+ bool DoSave(char* lpszPathName, bool bReplace);
+ bool DoFileSave();
+ bool FileLoad(File* file, bool bUndo, bool bMerge);
+ void FileSave(File* file, bool bUndo);
+ void FileReadLDraw(File* file, Matrix* prevmat, int* nOk, int DefColor, int* nStep);
+
+public:
+ // File helpers
+ bool OnNewDocument();
+ bool OnOpenDocument(char* lpszPathName);
+ bool SaveModified();
+
+protected:
+ // mail enabling
+// void OnFileSendMail();
+// void OnUpdateFileSendMail(CCmdUI* pCmdUI);
+
+ // TODO: Fix ! This is a hack to make things work now
+ friend class CCADView;
+ friend void PrintPiecesThread(void* pv);
+ friend void Export3DStudio();
+};
+
+#endif // _PROJECT_H_
diff --git a/common/quant.cpp b/common/quant.cpp
new file mode 100644
index 0000000..f32cc68
--- /dev/null
+++ b/common/quant.cpp
@@ -0,0 +1,639 @@
+///////////////////////////////////////
+// DL1 Quantization
+
+#include <stdlib.h>
+#include <math.h>
+#include <memory.h>
+#include "quant.h"
+
+//#define FAST // improves speed but uses a lot of memory
+#define QUAL1 // slightly improves quality
+//#define QUAL2 // slightly improves quality
+
+//#define DITHER1 // 1-val error diffusion dither
+#define DITHER2 // 2-val error diffusion dither
+//#define DITHER4 // 4-val error diffusion dither (Floyd-Steinberg)
+
+#define DITHER_MAX 20
+
+static void dlq_finish();
+static int build_table(unsigned char *image, unsigned long pixels);
+static void fixheap(unsigned long id);
+static void reduce_table(int num_colors);
+static void set_palette(int index, int level);
+static void closest_color(int index, int level);
+static int quantize_image(unsigned char *in, unsigned char *out, int width, int height, int dither);
+static int bestcolor(int r, int g, int b);
+
+static unsigned char palette[3][256];
+static CUBE *rgb_table[6];
+static unsigned short r_offset[256], g_offset[256], b_offset[256];
+static CLOSEST_INFO c_info;
+static int tot_colors, pal_index;
+static unsigned long *squares;
+static FCUBE *heap = NULL;
+static short *dl_image = NULL;
+
+bool dl1quant(unsigned char *inbuf, unsigned char *outbuf, int width, int height, int quant_to, int dither, unsigned char userpal[3][256])
+{
+ int i;
+
+ // dlq_init
+ for (i = 0; i < 6; i++)
+ rgb_table[i]=NULL;
+
+ tot_colors=0;
+ pal_index=0;
+
+ heap = NULL;
+ dl_image = NULL;
+
+ for (i = 0; i < 256; i++)
+ {
+ r_offset[i] = (i & 128) << 7 | (i & 64) << 5 | (i & 32) << 3 |
+ (i & 16) << 1 | (i & 8) >> 1;
+ g_offset[i] = (i & 128) << 6 | (i & 64) << 4 | (i & 32) << 2 |
+ (i & 16) << 0 | (i & 8) >> 2;
+ b_offset[i] = (i & 128) << 5 | (i & 64) << 3 | (i & 32) << 1 |
+ (i & 16) >> 1 | (i & 8) >> 3;
+ }
+
+ c_info.palette_index=0;
+ c_info.red=0;
+ c_info.green=0;
+ c_info.blue=0;
+ c_info.distance=0;
+
+ for (i = (-255); i <= 255; i++)
+ c_info.squares[i+255] = i*i;
+
+ for (i = 0; i < 256; i++)
+ {
+ palette[0][i] = 0;
+ palette[1][i] = 0;
+ palette[2][i] = 0;
+ }
+
+ squares = c_info.squares + 255;
+
+ // dlq_start
+ rgb_table[0] = (CUBE*)calloc(sizeof(CUBE), 1);
+ rgb_table[1] = (CUBE*)calloc(sizeof(CUBE), 8);
+ rgb_table[2] = (CUBE*)calloc(sizeof(CUBE), 64);
+ rgb_table[3] = (CUBE*)calloc(sizeof(CUBE), 512);
+ rgb_table[4] = (CUBE*)calloc(sizeof(CUBE), 4096);
+ rgb_table[5] = (CUBE*)calloc(sizeof(CUBE), 32768);
+
+ for (i = 0; i <= 5; i++)
+ if (rgb_table[i] == NULL)
+ {
+ dlq_finish();
+ return false;
+ }
+
+ pal_index = 0;
+
+ if (build_table(inbuf, width*height) == 0)
+ {
+ dlq_finish();
+ return false;
+ }
+
+ reduce_table(quant_to);
+ set_palette(0, 0);
+
+ if (quantize_image(inbuf, outbuf, width, height, dither) == 0)
+ {
+ dlq_finish();
+ return false;
+ }
+
+ dlq_finish();
+ for (i = 0; i < 256; i++)
+ {
+ userpal[0][i] = palette[0][i];
+ userpal[1][i] = palette[1][i];
+ userpal[2][i] = palette[2][i];
+ }
+
+ return true;
+}
+
+static void dlq_finish(void)
+{
+ for (int i = 0;i < 6;i++)
+ {
+ if (rgb_table[i] != NULL)
+ {
+ free(rgb_table[i]);
+ rgb_table[i] = NULL;
+ }
+ }
+
+ if (heap != NULL)
+ {
+ free(heap);
+ heap = NULL;
+ }
+
+ if (dl_image != NULL)
+ {
+ free(dl_image);
+ dl_image = NULL;
+ }
+
+ memset(&c_info, 0, sizeof(CLOSEST_INFO));
+
+ tot_colors=pal_index=0;
+}
+
+// returns 1 on success, 0 on failure
+static int build_table(unsigned char *image, unsigned long pixels)
+{
+ unsigned long i = 0, index = 0, cur_count = 0, head = 0, tail = 0;
+ long j = 0;
+
+ heap = (FCUBE *) malloc(sizeof(FCUBE) * 32769);
+ if (heap == NULL)
+ return 0;
+
+#ifdef FAST
+ dl_image = malloc(sizeof(short) * pixels);
+ if (dl_image == NULL)
+ return 0;
+#endif
+
+ for (i = 0; i < pixels; i++)
+ {
+#ifdef FAST
+ dl_image[i] = index = r_offset[image[0]] + g_offset[image[1]] + b_offset[image[2]];
+#else
+ index = r_offset[image[0]] + g_offset[image[1]] + b_offset[image[2]];
+#endif
+#ifdef QUAL1
+ rgb_table[5][index].r += image[0];
+ rgb_table[5][index].g += image[1];
+ rgb_table[5][index].b += image[2];
+#endif
+ rgb_table[5][index].pixel_count++;
+ image += 3;
+ }
+
+ tot_colors = 0;
+ for (i = 0; i < 32768; i++)
+ {
+ cur_count = rgb_table[5][i].pixel_count;
+ if (cur_count)
+ {
+ heap[++tot_colors].level = 5;
+ heap[tot_colors].index = (unsigned short)i;
+ rgb_table[5][i].pixels_in_cube = cur_count;
+#ifndef QUAL1
+ rgb_table[5][i].r = cur_count * (((i & 0x4000) >> 7 |
+ (i & 0x0800) >> 5 | (i & 0x0100) >> 3 |
+ (i & 0x0020) >> 1 | (i & 0x0004) << 1) + 4);
+ rgb_table[5][i].g = cur_count * (((i & 0x2000) >> 6 |
+ (i & 0x0400) >> 4 | (i & 0x0080) >> 2 |
+ (i & 0x0010) >> 0 | (i & 0x0002) << 2) + 4);
+ rgb_table[5][i].b = cur_count * (((i & 0x1000) >> 5 |
+ (i & 0x0200) >> 3 | (i & 0x0040) >> 1 |
+ (i & 0x0008) << 1 | (i & 0x0001) << 3) + 4);
+#endif
+ head = i;
+ for (j = 4; j >= 0; j--)
+ {
+ tail = head & 0x7;
+ head >>= 3;
+ rgb_table[j][head].pixels_in_cube += cur_count;
+ rgb_table[j][head].children |= 1 << tail;
+ }
+ }
+ }
+
+ for (i = tot_colors; i > 0; i--)
+ fixheap(i);
+
+ return 1;
+}
+
+static void fixheap(unsigned long id)
+{
+ unsigned char thres_level = heap[id].level;
+ unsigned long thres_index = heap[id].index, index = 0;
+ unsigned long half_totc = tot_colors >> 1;
+ unsigned long thres_val = rgb_table[thres_level][thres_index].pixels_in_cube;
+
+ while (id <= half_totc)
+ {
+ index = id << 1;
+
+ if (index < (unsigned long)tot_colors)
+ if (rgb_table[heap[index].level][heap[index].index].pixels_in_cube
+ > rgb_table[heap[index+1].level][heap[index+1].index].pixels_in_cube)
+ index++;
+
+ if (thres_val <= rgb_table[heap[index].level][heap[index].index].pixels_in_cube)
+ break;
+ else {
+ heap[id] = heap[index];
+ id = index;
+ }
+ }
+ heap[id].level = thres_level;
+ heap[id].index = (unsigned short)thres_index;
+}
+
+static void reduce_table(int num_colors)
+{
+ while (tot_colors > num_colors)
+ {
+ unsigned char tmp_level = heap[1].level, t_level = (tmp_level - 1) > 0 ? (tmp_level - 1) : 0;
+ unsigned long tmp_index = heap[1].index, t_index = tmp_index >> 3;
+
+ if (rgb_table[t_level][t_index].pixel_count)
+ heap[1] = heap[tot_colors--];
+ else
+ {
+ heap[1].level = t_level;
+ heap[1].index = (unsigned short)t_index;
+ }
+
+ rgb_table[t_level][t_index].pixel_count += rgb_table[tmp_level][tmp_index].pixel_count;
+ rgb_table[t_level][t_index].r += rgb_table[tmp_level][tmp_index].r;
+ rgb_table[t_level][t_index].g += rgb_table[tmp_level][tmp_index].g;
+ rgb_table[t_level][t_index].b += rgb_table[tmp_level][tmp_index].b;
+ rgb_table[t_level][t_index].children &= ~(1 << (tmp_index & 0x7));
+
+ fixheap(1);
+ }
+}
+
+static void set_palette(int index, int level)
+{
+ int i;
+
+ if (rgb_table[level][index].children)
+ for (i = 7; i >= 0; i--)
+ if (rgb_table[level][index].children & (1 << i))
+ set_palette((index << 3) + i, level + 1);
+
+ if (rgb_table[level][index].pixel_count)
+ {
+ unsigned long r_sum, g_sum, b_sum, sum;
+
+ rgb_table[level][index].palette_index = pal_index;
+
+ r_sum = rgb_table[level][index].r;
+ g_sum = rgb_table[level][index].g;
+ b_sum = rgb_table[level][index].b;
+
+ sum = rgb_table[level][index].pixel_count;
+
+ palette[0][pal_index] = (unsigned char)((r_sum + (sum >> 1)) / sum);
+ palette[1][pal_index] = (unsigned char)((g_sum + (sum >> 1)) / sum);
+ palette[2][pal_index] = (unsigned char)((b_sum + (sum >> 1)) / sum);
+
+ pal_index++;
+ }
+}
+
+static void closest_color(int index, int level)
+{
+ int i;
+
+ if (rgb_table[level][index].children)
+ for (i = 7; i >= 0; i--)
+ if (rgb_table[level][index].children & (1 << i))
+ closest_color((index << 3) + i, level + 1);
+
+ if (rgb_table[level][index].pixel_count)
+ {
+ long dist, r_dist, g_dist, b_dist;
+ unsigned char pal_num = rgb_table[level][index].palette_index;
+
+ // Determine if this color is "closest".
+ r_dist = palette[0][pal_num] - c_info.red;
+ g_dist = palette[1][pal_num] - c_info.green;
+ b_dist = palette[2][pal_num] - c_info.blue;
+
+ dist = squares[r_dist] + squares[g_dist] + squares[b_dist];
+
+ if (dist < (long)c_info.distance)
+ {
+ c_info.distance = dist;
+ c_info.palette_index = pal_num;
+ }
+ }
+}
+
+// returns 1 on success, 0 on failure
+static int quantize_image(unsigned char *in, unsigned char *out, int width, int height, int dither)
+{
+ if (!dither)
+ {
+ unsigned long i = 0, pixels = width * height;
+ unsigned short level = 0, index = 0;
+ unsigned char tmp_r = 0, tmp_g = 0, tmp_b = 0, cube = 0;
+ unsigned char *lookup = NULL;
+
+ lookup = (unsigned char*)malloc(sizeof(char) * 32768);
+ if (lookup == NULL)
+ return 0;
+
+ for (i = 0; i < 32768; i++)
+ if (rgb_table[5][i].pixel_count)
+ {
+ tmp_r = (unsigned char)((i & 0x4000) >> 7 | (i & 0x0800) >> 5 |
+ (i & 0x0100) >> 3 | (i & 0x0020) >> 1 |
+ (i & 0x0004) << 1);
+ tmp_g = (unsigned char)((i & 0x2000) >> 6 | (i & 0x0400) >> 4 |
+ (i & 0x0080) >> 2 | (i & 0x0010) >> 0 |
+ (i & 0x0002) << 2);
+ tmp_b = (unsigned char)((i & 0x1000) >> 5 | (i & 0x0200) >> 3 |
+ (i & 0x0040) >> 1 | (i & 0x0008) << 1 |
+ (i & 0x0001) << 3);
+ #ifdef QUAL2
+ lookup[i] = bestcolor(tmp_r, tmp_g, tmp_b);
+ #else
+ c_info.red = tmp_r + 4;
+ c_info.green = tmp_g + 4;
+ c_info.blue = tmp_b + 4;
+ level = 0;
+ index = 0;
+ for (;;) {
+ cube = (tmp_r&128) >> 5 | (tmp_g&128) >> 6 | (tmp_b&128) >> 7;
+ if ((rgb_table[level][index].children & (1 << cube)) == 0) {
+ c_info.distance = (unsigned long)~0L;
+ closest_color(index, level);
+ lookup[i] = c_info.palette_index;
+ break;
+ }
+ level++;
+ index = (index << 3) + cube;
+ tmp_r <<= 1;
+ tmp_g <<= 1;
+ tmp_b <<= 1;
+ }
+ #endif
+ }
+
+ for (i = 0; i < pixels; i++)
+ {
+ #ifdef FAST
+ out[i] = lookup[dl_image[i]];
+ #else
+ out[i] = lookup[r_offset[in[0]] + g_offset[in[1]] + b_offset[in[2]]];
+ in += 3;
+ #endif
+ }
+
+ free(lookup);
+
+ }
+ else // dither
+ {
+ #if defined(DITHER2) || defined(DITHER4)
+ long i = 0, j = 0;
+ long r_pix = 0, g_pix = 0, b_pix = 0;
+ long offset = 0, dir = 0;
+ long two_val = 0, odd_scanline = 0;
+ long err_len = (width + 2) * 3;
+ unsigned char *range_tbl = NULL, *range = NULL;
+ short *lookup = NULL, *erowerr = NULL, *orowerr = NULL;
+ short *thisrowerr = NULL, *nextrowerr = NULL;
+ char *dith_max_tbl = NULL, *dith_max = NULL;
+
+ lookup = (short*)malloc(sizeof(short) * 32768);
+ erowerr = (short*)malloc(sizeof(short) * err_len);
+ orowerr = (short*)malloc(sizeof(short) * err_len);
+ range_tbl = (unsigned char*)malloc(3 * 256);
+ range = range_tbl + 256;
+ dith_max_tbl= (char*)malloc(512);
+ dith_max = dith_max_tbl + 256;
+
+ if (range_tbl == NULL || lookup == NULL || erowerr == NULL || orowerr == NULL || dith_max_tbl == NULL)
+ {
+ if (range_tbl != NULL)
+ {
+ free(range_tbl);
+ range_tbl=NULL;
+ }
+ if (lookup != NULL)
+ {
+ free(lookup);
+ lookup=NULL;
+ }
+ if (erowerr != NULL)
+ {
+ free(erowerr);
+ erowerr=NULL;
+ }
+ if (orowerr != NULL)
+ {
+ free(orowerr);
+ orowerr=NULL;
+ }
+ if (dith_max_tbl != NULL)
+ {
+ free(dith_max_tbl);
+ dith_max_tbl=NULL;
+ }
+ return 0;
+ }
+
+ for (i = 0; i < err_len; i++)
+ erowerr[i] = 0;
+
+ for (i = 0; i < 32768; i++)
+ lookup[i] = -1;
+
+ for (i = 0; i < 256; i++)
+ {
+ range_tbl[i] = 0;
+ range_tbl[i + 256] = (unsigned char) i;
+ range_tbl[i + 512] = 255;
+ }
+
+ for (i = 0; i < 256; i++)
+ {
+ dith_max_tbl[i] = -DITHER_MAX;
+ dith_max_tbl[i + 256] = DITHER_MAX;
+ }
+ for (i = -DITHER_MAX; i <= DITHER_MAX; i++)
+ dith_max_tbl[i + 256] = (char)i;
+
+ for (i = 0 ; i < height; i++)
+ {
+ if (odd_scanline)
+ {
+ dir = -1;
+ in += (width - 1) * 3;
+ out += (width - 1);
+ thisrowerr = orowerr + 3;
+ nextrowerr = erowerr + width * 3;
+ }
+ else
+ {
+ dir = 1;
+ thisrowerr = erowerr + 3;
+ nextrowerr = orowerr + width * 3;
+ }
+
+ nextrowerr[0] = nextrowerr[1] = nextrowerr[2] = 0;
+
+ for (j = 0; j < width; j++)
+ {
+ #ifdef DITHER2
+ r_pix = range[(thisrowerr[0] >> 1) + in[0]];
+ g_pix = range[(thisrowerr[1] >> 1) + in[1]];
+ b_pix = range[(thisrowerr[2] >> 1) + in[2]];
+ #else
+ r_pix = range[((thisrowerr[0] + 8) >> 4) + in[0]];
+ g_pix = range[((thisrowerr[1] + 8) >> 4) + in[1]];
+ b_pix = range[((thisrowerr[2] + 8) >> 4) + in[2]];
+ #endif
+ offset = (r_pix&248) << 7 | (g_pix&248) << 2 | b_pix >> 3;
+ if (lookup[offset] < 0)
+ lookup[offset] = bestcolor(r_pix, g_pix, b_pix);
+ *out = (unsigned char)lookup[offset];
+ r_pix = dith_max[r_pix - palette[0][lookup[offset]]];
+ g_pix = dith_max[g_pix - palette[1][lookup[offset]]];
+ b_pix = dith_max[b_pix - palette[2][lookup[offset]]];
+
+ #ifdef DITHER2
+ nextrowerr[0 ] = (short)r_pix;
+ thisrowerr[0+3] += (short)r_pix;
+ nextrowerr[1 ] = (short)g_pix;
+ thisrowerr[1+3] += (short)g_pix;
+ nextrowerr[2 ] = (short)b_pix;
+ thisrowerr[2+3] += (short)b_pix;
+ #else
+ two_val = r_pix * 2;
+ nextrowerr[0-3] = r_pix;
+ r_pix += two_val;
+ nextrowerr[0+3] += r_pix;
+ r_pix += two_val;
+ nextrowerr[0 ] += r_pix;
+ r_pix += two_val;
+ thisrowerr[0+3] += r_pix;
+ two_val = g_pix * 2;
+ nextrowerr[1-3] = g_pix;
+ g_pix += two_val;
+ nextrowerr[1+3] += g_pix;
+ g_pix += two_val;
+ nextrowerr[1 ] += g_pix;
+ g_pix += two_val;
+ thisrowerr[1+3] += g_pix;
+ two_val = b_pix * 2;
+ nextrowerr[2-3] = b_pix;
+ b_pix += two_val;
+ nextrowerr[2+3] += b_pix;
+ b_pix += two_val;
+ nextrowerr[2 ] += b_pix;
+ b_pix += two_val;
+ thisrowerr[2+3] += b_pix;
+ #endif
+ thisrowerr += 3;
+ nextrowerr -= 3;
+ in += dir * 3;
+ out += dir;
+ }
+
+ if ((i % 2) == 1)
+ {
+ in += (width + 1) * 3;
+ out += (width + 1);
+ }
+
+ odd_scanline = !odd_scanline;
+ }
+
+ free(range_tbl);
+ free(lookup);
+ free(erowerr);
+ free(orowerr);
+ free(dith_max_tbl);
+ #else
+ long i = 0, j = 0;
+ long r_pix = 0, g_pix = 0, b_pix=0;
+ long r_err = 0, g_err = 0, b_err=0;
+ long offset = 0;
+ BYTE *range_tbl = (BYTE*)malloc(3 * 256), *range = range_tbl + 256;
+ short *lookup = (sshort *)malloc(sizeof(short) * 32768);
+
+ if (range_tbl == NULL || lookup == NULL)
+ {
+ if (range_tbl != NULL)
+ free(range_tbl);
+ if (lookup != NULL)
+ free(lookup);
+ return 0;
+ }
+
+ for (i = 0; i < 32768; i++)
+ lookup[i] = -1;
+
+ for (i = 0; i < 256; i++)
+ {
+ range_tbl[i] = 0;
+ range_tbl[i + 256] = (BYTE) i;
+ range_tbl[i + 512] = 255;
+ }
+
+ for (i = 0; i < height; i++)
+ {
+ r_err = g_err = b_err = 0;
+ for (j = width - 1; j >= 0; j--)
+ {
+ r_pix = range[(r_err >> 1) + in[0]];
+ g_pix = range[(g_err >> 1) + in[1]];
+ b_pix = range[(b_err >> 1) + in[2]];
+
+ offset = (r_pix&248) << 7 | (g_pix&248) << 2 | b_pix >> 3;
+
+ if (lookup[offset] < 0)
+ lookup[offset] = bestcolor(r_pix, g_pix, b_pix);
+
+ *out++ = (unsigned char)lookup[offset];
+
+ r_err = r_pix - palette[0][lookup[offset]];
+ g_err = g_pix - palette[1][lookup[offset]];
+ b_err = b_pix - palette[2][lookup[offset]];
+
+ in += 3;
+ }
+ }
+
+ free(range_tbl);
+ free(lookup);
+ #endif
+ }
+
+ return 1;
+}
+
+static int bestcolor(int r, int g, int b)
+{
+ unsigned long i = 0, bestcolor = 0, curdist = 0, mindist = 0;
+ long rdist = 0, gdist = 0, bdist = 0;
+
+ r = (r & 248) + 4;
+ g = (g & 248) + 4;
+ b = (b & 248) + 4;
+ mindist = 200000;
+
+ for (i = 0; i < (unsigned long)tot_colors; i++)
+ {
+ rdist = palette[0][i] - r;
+ gdist = palette[1][i] - g;
+ bdist = palette[2][i] - b;
+ curdist = squares[rdist] + squares[gdist] + squares[bdist];
+
+ if (curdist < mindist)
+ {
+ mindist = curdist;
+ bestcolor = i;
+ }
+ }
+ return bestcolor;
+}
diff --git a/common/quant.h b/common/quant.h
new file mode 100644
index 0000000..b1609b2
--- /dev/null
+++ b/common/quant.h
@@ -0,0 +1,31 @@
+///////////////////////////////////////
+// DL1 Quantization
+
+#ifndef _QUANT_H_
+#define _QUANT_H_
+
+typedef struct
+{
+ unsigned long r, g, b;
+ unsigned long pixel_count;
+ unsigned long pixels_in_cube;
+ unsigned char children;
+ unsigned char palette_index;
+} CUBE;
+
+typedef struct
+{
+ unsigned char level;
+ unsigned short index;
+} FCUBE;
+
+typedef struct
+{
+ unsigned char palette_index, red, green, blue;
+ unsigned long distance;
+ unsigned long squares[255+255+1];
+} CLOSEST_INFO;
+
+bool dl1quant(unsigned char *inbuf, unsigned char *outbuf, int width, int height, int quant_to, int dither, unsigned char userpal[3][256]);
+
+#endif // _QUANT_H_
diff --git a/common/terrain.cpp b/common/terrain.cpp
new file mode 100644
index 0000000..771cce0
--- /dev/null
+++ b/common/terrain.cpp
@@ -0,0 +1,855 @@
+// Terrain: a Bezier surface.
+//
+
+#ifdef _WINDOWS
+#include "stdafx.h"
+#else
+#include <GL/gl.h>
+#endif
+#include <math.h>
+#include <stdlib.h>
+#include <time.h>
+#include "defines.h"
+#include "terrain.h"
+#include "file.h"
+#include "camera.h"
+#include "matrix.h"
+#include "system.h"
+#include "texture.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// Static functions
+
+// Cubic Bezier patch matrix:
+// 1 0 0 0
+// -3 3 0 0
+// 3 -6 3 0
+// -1 3 -3 1
+static float SolveBase(int i, float t)
+{
+ switch (i)
+ {
+ case 0: return (((-t)+3)*t-3)*t+1;
+ case 1: return (((3*t)-6)*t+3)*t;
+ case 2: return ((-3*t)+3)*t*t;
+ case 3: return t*t*t;
+ }
+ return 0;
+}
+
+static float SolveDiff(int i, float t)
+{
+ switch (i)
+ {
+ case 0: return ((-3*t)+6)*t-3;
+ case 1: return ((9*t)-12)*t+3;
+ case 2: return ((-9*t)+6)*t;
+ case 3: return 3*t*t;
+ }
+ return 0;
+}
+
+static bool boxIsOutside(const PATCHBOX box, const float plane[4])
+{
+ float planeEqVal;
+ int i;
+ for (i = 0; i < 8; i++)
+ {
+ planeEqVal =
+ plane[0]*box.corners[i][0] +
+ plane[1]*box.corners[i][1] +
+ plane[2]*box.corners[i][2] + plane[3];
+ if (planeEqVal > 0)
+ return false;
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// PATCH functions
+
+void PATCHBOX::InitBox(float minX, float maxX, float minY, float maxY, float minZ, float maxZ)
+{
+ this->minX = minX;
+ this->maxY = maxY;
+ this->minY = minY;
+ this->maxX = maxX;
+ this->minZ = minZ;
+ this->maxZ = maxZ;
+
+ // Now make each corner point for convenient culling.
+ corners[0][0] = minX;
+ corners[1][0] = minX;
+ corners[2][0] = minX;
+ corners[3][0] = minX;
+ corners[4][0] = maxX;
+ corners[5][0] = maxX;
+ corners[6][0] = maxX;
+ corners[7][0] = maxX;
+
+ corners[0][1] = minY;
+ corners[1][1] = minY;
+ corners[2][1] = maxY;
+ corners[3][1] = maxY;
+ corners[4][1] = minY;
+ corners[5][1] = minY;
+ corners[6][1] = maxY;
+ corners[7][1] = maxY;
+
+ corners[0][2] = minZ;
+ corners[1][2] = maxZ;
+ corners[2][2] = minZ;
+ corners[3][2] = maxZ;
+ corners[4][2] = minZ;
+ corners[5][2] = maxZ;
+ corners[6][2] = minZ;
+ corners[7][2] = maxZ;
+}
+
+#define controlsX(b, a) control[(a*4+b)*3]
+#define controlsY(b, a) control[(a*4+b)*3+1]
+#define controlsZ(b, a) control[(a*4+b)*3+2]
+
+void PATCH::Tesselate(bool bNormals)
+{
+ FreeMemory();
+
+ vertex = new float[steps*steps*3];
+ if (bNormals)
+ normals = new float[steps*steps*3];
+
+ coords = new float[steps*steps*2];
+
+ float invTotalSteps = 1.0f / (steps - 1);
+
+ for (int stepU = 0; stepU < steps; stepU++)
+ {
+ // Generate the parameter for this step of the curve.
+ float u = stepU * invTotalSteps;
+
+ for (int stepV = 0; stepV < steps; stepV++)
+ {
+ // Generate the parameter for this step of the curve.
+ float v = stepV * invTotalSteps;
+
+ // This holds the point we're working on as we add control points' contributions to it.
+ float curPt[3] = { 0, 0, 0 };
+ float curNorm[3] = { 0, 0, 0 };
+ float curUTan[3] = { 0, 0, 0 };
+ float curVTan[3] = { 0, 0, 0 };
+
+ // Generate a point on the curve for this step.
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ {
+ // Get a few basis function values and products thereof that we'll need.
+ float bu = SolveBase(i, u);
+ float bv = SolveBase(j, v);
+ float dbu = SolveDiff(i, u);
+ float dbv = SolveDiff(j, v);
+ float bu_bv = bu * bv;
+ float bu_dbv = bu * dbv;
+ float dbu_bv = dbu * bv;
+
+ // Add this control point's contribution onto the current point.
+ curPt[0] += controlsX(i, j) * bu_bv;
+ curPt[1] += controlsY(i, j) * bu_bv;
+ curPt[2] += controlsZ(i, j) * bu_bv;
+
+ // Add this point's contribution to our u-tangent.
+ curUTan[0] += controlsX(i, j) * dbu_bv;
+ curUTan[1] += controlsY(i, j) * dbu_bv;
+ curUTan[2] += controlsZ(i, j) * dbu_bv;
+
+ // Add this point's contribution to our v-tangent.
+ curVTan[0] += controlsX(i, j) * bu_dbv;
+ curVTan[1] += controlsY(i, j) * bu_dbv;
+ curVTan[2] += controlsZ(i, j) * bu_dbv;
+ }
+
+ // Now get our normal as the cross-product of the u and v tangents.
+ curNorm[0] = curVTan[1] * curUTan[2] - curVTan[2] * curUTan[1];
+ curNorm[1] = curVTan[2] * curUTan[0] - curVTan[0] * curUTan[2];
+ curNorm[2] = curVTan[0] * curUTan[1] - curVTan[1] * curUTan[0];
+
+ // Normalize our normal (ouch!)
+ float rInv = 1.0f / (float)sqrt(curNorm[0]*curNorm[0] + curNorm[1]*curNorm[1] + curNorm[2]*curNorm[2]);
+ curNorm[0] *= rInv;
+ curNorm[1] *= rInv;
+ curNorm[2] *= rInv;
+
+ // Store these.
+ memcpy(&vertex[(stepU+(stepV*steps))*3], curPt, 3*sizeof(float));
+ if (bNormals)
+ memcpy(&normals[(stepU+(stepV*steps))*3], curNorm, 3*sizeof(float));
+
+ coords[(stepU+(stepV*steps))*2] = u;
+ coords[(stepU+(stepV*steps))*2+1] = v;
+ }
+ }
+
+ index = new unsigned short[(steps-1)*(steps-1)*6];
+
+ for (unsigned short i = 0; i < steps-1; i++)
+ for (unsigned short j = 0; j < steps-1; j++)
+ {
+ index[(i*(steps-1)+j)*6] = i*steps+j;
+ index[(i*(steps-1)+j)*6+1] = (i+1)*steps+j;
+ index[(i*(steps-1)+j)*6+2] = i*steps+j+1;
+ index[(i*(steps-1)+j)*6+3] = (i+1)*steps+j;
+ index[(i*(steps-1)+j)*6+4] = (i+1)*steps+j+1;
+ index[(i*(steps-1)+j)*6+5] = i*steps+j+1;
+ }
+}
+
+#undef controlsX
+#undef controlsY
+#undef controlsZ
+
+void PATCH::FreeMemory()
+{
+ if (vertex)
+ {
+ delete[] vertex;
+ vertex = NULL;
+ }
+
+ if (normals)
+ {
+ delete[] normals;
+ normals = NULL;
+ }
+
+ if (coords)
+ {
+ delete[] coords;
+ coords = NULL;
+ }
+
+ if (index)
+ {
+ delete[] index;
+ index = NULL;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Terrain construction/destruction
+
+Terrain::Terrain()
+{
+ m_uPatches = 0;
+ m_vPatches = 0;
+ m_uSize = 50;
+ m_vSize = 50;
+ m_Patches = NULL;
+ m_pControl = NULL;
+ m_nOptions = 0;
+ m_pTexture = new Texture();
+}
+
+Terrain::~Terrain()
+{
+ FreeMemory();
+ delete m_pTexture;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Terrain functions
+
+void Terrain::FileLoad(File* file)
+{
+ unsigned char ch;
+ unsigned short sh;
+ int i, j;
+
+ file->Read(&ch, 1);
+ file->Read(&i, 4);
+ file->Read(&j, 4);
+ file->Read(&m_uSize, 4);
+ file->Read(&m_vSize, 4);
+ file->Read(&m_nOptions, 4);
+ file->Read(&m_fColor, 12);
+
+ if (ch == 1)
+ {
+ file->Read(&ch, 1);
+ sh = ch;
+ }
+ else
+ file->Read(&sh, 2);
+
+ if (sh > LC_MAXPATH)
+ file->Seek(sh, SEEK_CUR);
+ else
+ file->Read(&m_strTexture, sh);
+
+ SetPatchCount(i, j);
+ for (i = 0; i < GetCountU(); i++)
+ for (j = 0; j < GetCountV(); j++)
+ file->Read(&m_pControl[i][j*3+2], 4);
+}
+
+void Terrain::FileSave(File* file)
+{
+ unsigned char version = 2; // LeoCAD 0.70
+ unsigned short sh;
+
+ file->Write(&version, 1);
+ file->Write(&m_uPatches, 4);
+ file->Write(&m_vPatches, 4);
+ file->Write(&m_uSize, 4);
+ file->Write(&m_vSize, 4);
+ file->Write(&m_nOptions, 4);
+ file->Write(&m_fColor, 12);
+
+ sh = strlen(m_strTexture);
+ file->Write(&sh, 2);
+ file->Write(m_strTexture, sh);
+
+ for (int i = 0; i < GetCountU(); i++)
+ for (int j = 0; j < GetCountV(); j++)
+ file->Write(&m_pControl[i][j*3+2], 4);
+}
+
+void Terrain::FreeMemory()
+{
+ int i;
+
+ if (m_Patches)
+ {
+ for (i = 0; i < m_uPatches; i++)
+ {
+ for (int j = 0; j < m_vPatches; j++)
+ m_Patches[i][j].FreeMemory();
+
+ delete[] m_Patches[i];
+ }
+
+ delete[] m_Patches;
+ m_Patches = NULL;
+ }
+
+ if (m_pControl)
+ {
+ for (i = 0; i < (m_uPatches*3)+1; i++)
+ delete[] m_pControl[i];
+ delete[] m_pControl;
+
+ m_pControl = NULL;
+ }
+}
+
+// Copy terrain info
+Terrain& Terrain::operator= (const Terrain& source)
+{
+ FreeMemory();
+ int i;
+
+ m_nOptions = source.m_nOptions;
+ strcpy(m_strTexture, source.m_strTexture);
+ memcpy(m_fColor, source.m_fColor, sizeof(m_fColor));
+ m_uPatches = source.m_uPatches;
+ m_vPatches = source.m_vPatches;
+
+ m_Patches = new PATCH*[m_uPatches];
+ for (i = 0; i < m_uPatches; i++)
+ m_Patches[i] = new PATCH[m_vPatches];
+
+ int uCount = GetCountU(), vCount = GetCountV();
+
+ m_pControl = new float*[uCount];
+ for (i = 0; i < uCount; i++)
+ {
+ m_pControl[i] = new float[vCount*3];
+ memcpy(m_pControl[i], source.m_pControl[i], vCount*3*sizeof(float));
+ }
+
+ m_uSize = source.m_uSize;
+ m_vSize = source.m_vSize;
+
+ SetControlPoints();
+ Tesselate();
+
+ return *this;
+}
+
+void Terrain::GetSize(float *uSize, float *vSize)
+{
+ *uSize = m_uSize;
+ *vSize = m_vSize;
+}
+
+void Terrain::SetSize(float uSize, float vSize)
+{
+ m_uSize = uSize;
+ m_vSize = vSize;
+
+ int i, j, uCount = GetCountU(), vCount = GetCountV();
+
+ for (i = 0; i < uCount; i++)
+ for (j = 0; j < vCount; j++)
+ {
+ m_pControl[i][j*3] = m_uSize * ((float)i/(uCount-1)-0.5f);
+ m_pControl[i][j*3+1] = m_vSize * ((float)j/(vCount-1)-0.5f);
+ }
+
+ SetControlPoints();
+ Tesselate();
+}
+
+void Terrain::GetPatchCount(int *uCount, int *vCount)
+{
+ *uCount = m_uPatches;
+ *vCount = m_vPatches;
+}
+
+void Terrain::SetPatchCount(int uPatches, int vPatches)
+{
+ if (uPatches == m_uPatches && vPatches == m_vPatches)
+ return;
+
+ float** oldControl = m_pControl;
+ int i, j, uCount = uPatches*3+1, vCount = vPatches*3+1;
+ int uCountOld = m_uPatches != 0 ? m_uPatches*3+1 : 0, vCountOld = m_vPatches != 0 ? m_vPatches*3+1 : 0;
+
+ // allocate new arrays
+// if (uPatches != m_uPatches)
+ m_pControl = new float*[uCount];
+
+ if (m_vPatches != vPatches)
+ {
+ for (i = 0; i < uCount; i++)
+ m_pControl[i] = new float[vCount*3];
+ }
+ else
+ {
+ for (i = 0; (i < uCount) && (i < uCountOld); i++)
+ m_pControl[i] = oldControl[i];
+ for (i = uCountOld; i < uCount; i++)
+ m_pControl[i] = new float[vCount*3];
+ }
+
+ // set the points
+ for (i = 0; i < uCount; i++)
+ {
+ for (j = 0; j < vCount; j++)
+ {
+ m_pControl[i][j*3] = m_uSize * ((float)i/(uCount-1)-0.5f);
+ m_pControl[i][j*3+1] = m_vSize * ((float)j/(vCount-1)-0.5f);
+
+ if (i < uCountOld && j < vCountOld)
+ m_pControl[i][j*3+2] = oldControl[i][j*3+2];
+ else
+ m_pControl[i][j*3+2] = 0;
+ }
+ }
+
+ if (m_vPatches != vPatches)
+ {
+ for (i = 0; i < uCountOld; i++)
+ delete[] oldControl[i];
+ }
+ else
+ {
+ for (i = uCount; i < uCountOld; i++)
+ delete[] oldControl[i];
+ }
+
+// if ((uPatches != m_uPatches) && (oldControl != NULL))
+ delete[] oldControl;
+
+ if (m_Patches)
+ {
+ for (i = 0; i < m_uPatches; i++)
+ {
+ for (j = 0; j < m_vPatches; j++)
+ m_Patches[i][j].FreeMemory();
+
+ delete[] m_Patches[i];
+ }
+
+ delete[] m_Patches;
+ }
+
+ m_uPatches = uPatches;
+ m_vPatches = vPatches;
+
+ m_Patches = new PATCH*[m_uPatches];
+ for (i = 0; i < m_uPatches; i++)
+ m_Patches[i] = new PATCH[m_vPatches];
+
+ SetControlPoints();
+ Tesselate();
+}
+
+// Set the control points for each patch
+void Terrain::SetControlPoints()
+{
+ int i, j;
+
+ for (i = 0; i < m_uPatches; i++)
+ for (j = 0; j < m_vPatches; j++)
+ {
+///////////
+ m_Patches[i][j].FreeMemory();
+
+ float minX = 9999999, maxX = -9999999, minY = 9999999, maxY = -9999999, minZ = 9999999, maxZ = -9999999;
+
+ for (int a = 0; a < 4; a++)
+ for (int b = 0; b < 4; b++)
+ {
+ m_Patches[i][j].control[(a*4+b)*3] = m_pControl[i*(4-1)+a][(j*(4-1)+b)*3];
+ m_Patches[i][j].control[(a*4+b)*3+1] = m_pControl[i*(4-1)+a][(j*(4-1)+b)*3+1];
+ m_Patches[i][j].control[(a*4+b)*3+2] = m_pControl[i*(4-1)+a][(j*(4-1)+b)*3+2];
+
+ minX = min(minX, m_Patches[i][j].control[(a*4+b)*3]);
+ maxX = max(maxX, m_Patches[i][j].control[(a*4+b)*3]);
+ minY = min(minY, m_Patches[i][j].control[(a*4+b)*3+1]);
+ maxY = max(maxY, m_Patches[i][j].control[(a*4+b)*3+1]);
+ minZ = min(minZ, m_Patches[i][j].control[(a*4+b)*3+2]);
+ maxZ = max(maxZ, m_Patches[i][j].control[(a*4+b)*3+2]);
+ }
+ m_Patches[i][j].box.InitBox(minX, maxX, minY, maxY, minZ, maxZ);
+ }
+}
+
+// Generate mesh and store for later use.
+void Terrain::Tesselate()
+{
+ int i, j, a, steps = 10;
+ float x, y, z, inv;
+
+ for (i = 0; i < m_uPatches; i++)
+ for (j = 0; j < m_vPatches; j++)
+ m_Patches[i][j].Tesselate((m_nOptions & LC_TERRAIN_SMOOTH) != 0);
+
+ if ((m_nOptions & LC_TERRAIN_SMOOTH) != 0)
+ {
+ // fix normals at +u
+ for (i = 0; i < m_uPatches-1; i++)
+ for (j = 0; j < m_vPatches; j++)
+ for (a = 0; a < steps; a++)
+ {
+ x = m_Patches[i][j].normals[((steps-1)*steps+a)*3] + m_Patches[i+1][j].normals[a*3];
+ y = m_Patches[i][j].normals[((steps-1)*steps+a)*3+1] + m_Patches[i+1][j].normals[a*3+1];
+ z = m_Patches[i][j].normals[((steps-1)*steps+a)*3+2] + m_Patches[i+1][j].normals[a*3+2];
+
+ inv = 1.0f / (float)sqrt(x*x + y*y + z*z);
+ x *= inv;
+ y *= inv;
+ z *= inv;
+
+ m_Patches[i][j].normals[((steps-1)*steps+a)*3] = x;
+ m_Patches[i][j].normals[((steps-1)*steps+a)*3+1] = y;
+ m_Patches[i][j].normals[((steps-1)*steps+a)*3+2] = z;
+ m_Patches[i+1][j].normals[a*3] = x;
+ m_Patches[i+1][j].normals[a*3+1] = y;
+ m_Patches[i+1][j].normals[a*3+2] = z;
+ }
+
+ // and at +v
+ for (i = 0; i < m_uPatches; i++)
+ for (j = 0; j < m_vPatches-1; j++)
+ for (a = 0; a < steps; a++)
+ {
+ x = m_Patches[i][j].normals[((steps-1)+a*steps)*3] + m_Patches[i][j+1].normals[(a*steps)*3];
+ y = m_Patches[i][j].normals[((steps-1)+a*steps)*3+1] + m_Patches[i][j+1].normals[(a*steps)*3+1];
+ z = m_Patches[i][j].normals[((steps-1)+a*steps)*3+2] + m_Patches[i][j+1].normals[(a*steps)*3+2];
+
+ inv = 1.0f / (float)sqrt(x*x + y*y + z*z);
+ x *= inv;
+ y *= inv;
+ z *= inv;
+
+ m_Patches[i][j].normals[((steps-1)+a*steps)*3] = x;
+ m_Patches[i][j].normals[((steps-1)+a*steps)*3+1] = y;
+ m_Patches[i][j].normals[((steps-1)+a*steps)*3+2] = z;
+ m_Patches[i][j+1].normals[(a*steps)*3] = x;
+ m_Patches[i][j+1].normals[(a*steps)*3+1] = y;
+ m_Patches[i][j+1].normals[(a*steps)*3+2] = z;
+ }
+ }
+}
+
+void Terrain::Render(Camera* pCam, float aspect)
+{
+ if (m_nOptions & LC_TERRAIN_FLAT)
+ {
+ float eye[3];
+ pCam->GetEye(eye);
+ glPushMatrix();
+ glTranslatef(eye[0], eye[1], 0);
+ glScalef(pCam->m_zFar, pCam->m_zFar, 1);
+
+ if (m_nOptions & LC_TERRAIN_TEXTURE)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ m_pTexture->MakeCurrent();
+ glEnable(GL_TEXTURE_2D);
+
+float tw = 15.0f, th = 15.0f;
+// tw = 2*pCam->m_zFar/m_nBackgroundSize;
+// th = 2*pCam->m_zFar/m_nBackgroundSize;
+
+float tx, ty;
+tx = (tw*eye[0])/(2*pCam->m_zFar);
+ty = (th*eye[1])/(2*pCam->m_zFar);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(tx, ty);
+ glVertex2f(-1, -1);
+ glTexCoord2f(tx+tw, ty);
+ glVertex2f(1, -1);
+ glTexCoord2f(tx+tw, ty+th);
+ glVertex2f(1, 1);
+ glTexCoord2f(tx, ty+th);
+ glVertex2f(-1, 1);
+ glEnd();
+
+ glDisable(GL_TEXTURE_2D);
+ }
+ else
+ {
+ glColor3fv(m_fColor);
+ glBegin(GL_QUADS);
+ glVertex2f(-1, -1);
+ glVertex2f(1, -1);
+ glVertex2f(1, 1);
+ glVertex2f(-1, 1);
+ glEnd();
+ }
+
+ glPopMatrix();
+ }
+ else
+ {
+ FindVisiblePatches(pCam, aspect);
+
+ int i, j;
+ glColor3fv(m_fColor);
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ if (m_nOptions & LC_TERRAIN_TEXTURE)
+ {
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ m_pTexture->MakeCurrent();
+ glEnable(GL_TEXTURE_2D);
+ }
+
+ if (m_nOptions & LC_TERRAIN_SMOOTH)
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ for (i = 0; i < m_uPatches; i++)
+ for (j = 0; j < m_vPatches; j++)
+ if (m_Patches[i][j].visible)
+ {
+ glVertexPointer(3, GL_FLOAT, 0, m_Patches[i][j].vertex);
+
+ if (m_nOptions & LC_TERRAIN_SMOOTH)
+ glNormalPointer(GL_FLOAT, 0, m_Patches[i][j].normals);
+
+ if (m_nOptions & LC_TERRAIN_TEXTURE)
+ glTexCoordPointer(2, GL_FLOAT, 0, m_Patches[i][j].coords);
+
+ glDrawElements(GL_TRIANGLES, (m_Patches[i][j].steps-1)*(m_Patches[i][j].steps-1)*6, GL_UNSIGNED_SHORT, m_Patches[i][j].index);
+ }
+
+ if (m_nOptions & LC_TERRAIN_SMOOTH)
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ if (m_nOptions & LC_TERRAIN_TEXTURE)
+ {
+ glDisable(GL_TEXTURE_2D);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ }
+ }
+}
+
+void Terrain::FindVisiblePatches(Camera* pCam, float aspect)
+{
+ // Get camera position information.
+ float eye[3];
+ pCam->GetEye(eye);
+
+ // Get perspective information.
+ float alpha = pCam->m_fovy / 2.0f;
+ float halfFovY = pCam->m_fovy / 2.0f;
+ halfFovY = halfFovY * 3.1415f / 180.0f;
+ float halfFovX = (float)atan(tan(halfFovY) * aspect);
+ halfFovX = halfFovX * 180.0f / 3.1415f;
+ float beta = 2.0f * halfFovX;
+
+ // Get vector stuff from the position.
+ float nonOrthoTop[3], target[3];
+ pCam->GetUp(nonOrthoTop);
+ pCam->GetTarget(target);
+ float front[3] = { target[0] - eye[0], target[1] - eye[1], target[2] - eye[2]};
+ float side[3];
+ side[0] = nonOrthoTop[1]*front[2] - nonOrthoTop[2]*front[1];
+ side[1] = nonOrthoTop[2]*front[0] - nonOrthoTop[0]*front[2];
+ side[2] = nonOrthoTop[0]*front[1] - nonOrthoTop[1]*front[0];
+
+ // Make sure our up vector is orthogonal.
+ float top[3];
+ top[0] = front[1]*side[2] - front[2]*side[1];
+ top[1] = front[2]*side[0] - front[0]*side[2];
+ top[2] = front[0]*side[1] - front[1]*side[0];
+
+ // Get our plane normals.
+ Matrix mat;
+ float topNormal[3] = { -top[0], -top[1], -top[2] };
+ mat.FromAxisAngle(side, -alpha);
+ mat.TransformPoints(topNormal, 1);
+
+ float bottomNormal[3] = { top[0], top[1], top[2] };
+ mat.FromAxisAngle(side, alpha);
+ mat.TransformPoints(bottomNormal, 1);
+
+ float rightNormal[3] = { side[0], side[1], side[2] };
+ mat.FromAxisAngle(top, -beta);
+ mat.TransformPoints(rightNormal, 1);
+
+ float leftNormal[3] = { -side[0], -side[1], -side[2] };
+ mat.FromAxisAngle(top, beta);
+ mat.TransformPoints(leftNormal, 1);
+
+ float nearNormal[3] = { front[0], front[1], front[2] };
+
+ // Now calculate our plane offsets from the normals and the eye position.
+ float topD = eye[0]*-topNormal[0] + eye[1]*-topNormal[1] + eye[2]*-topNormal[2];
+ float bottomD = eye[0]*-bottomNormal[0] + eye[1]*-bottomNormal[1] + eye[2]*-bottomNormal[2];
+ float leftD = eye[0]*-leftNormal[0] + eye[1]*-leftNormal[1] + eye[2]*-leftNormal[2];
+ float rightD = eye[0]*-rightNormal[0] + eye[1]*-rightNormal[1] + eye[2]*-rightNormal[2];
+ float nearD = eye[0]*-nearNormal[0] + eye[1]*-nearNormal[1] + eye[2]*-nearNormal[2];
+
+ // For the far plane, find the point farDist away from the eye along the front vector.
+ float farDist = pCam->m_zFar;
+ float farPt[3] = { front[0], front[1], front[2] };
+ float invR = farDist/(float)sqrt(farPt[0]*farPt[0]+farPt[1]*farPt[1]+farPt[2]*farPt[2]);
+ farPt[0] = farPt[0]*invR;
+ farPt[1] = farPt[1]*invR;
+ farPt[2] = farPt[2]*invR;
+ farPt[0] += eye[0];
+ farPt[1] += eye[1];
+ farPt[2] += eye[2];
+ float farD = farPt[0]*nearNormal[0] + farPt[1]*nearNormal[1] + farPt[2]*nearNormal[2];
+
+ // Now generate the planes
+ invR = 1.0f/(float)sqrt(topNormal[0]*topNormal[0]+topNormal[1]*topNormal[1]+topNormal[2]*topNormal[2]);
+ float topPlane[4] = { topNormal[0]*invR, topNormal[1]*invR, topNormal[2]*invR, topD*invR };
+ invR = 1.0f/(float)sqrt(bottomNormal[0]*bottomNormal[0]+bottomNormal[1]*bottomNormal[1]+bottomNormal[2]*bottomNormal[2]);
+ float bottomPlane[4] = { bottomNormal[0]*invR, bottomNormal[1]*invR, bottomNormal[2]*invR, bottomD*invR };
+ invR = 1.0f/(float)sqrt(leftNormal[0]*leftNormal[0]+leftNormal[1]*leftNormal[1]+leftNormal[2]*leftNormal[2]);
+ float leftPlane[4] = { leftNormal[0]*invR, leftNormal[1]*invR, leftNormal[2]*invR, leftD*invR };
+ invR = 1.0f/(float)sqrt(rightNormal[0]*rightNormal[0]+rightNormal[1]*rightNormal[1]+rightNormal[2]*rightNormal[2]);
+ float rightPlane[4] = { rightNormal[0]*invR, rightNormal[1]*invR, rightNormal[2]*invR, rightD*invR };
+ invR = 1.0f/(float)sqrt(nearNormal[0]*nearNormal[0]+nearNormal[1]*nearNormal[1]+nearNormal[2]*nearNormal[2]);
+ float nearPlane[4] = { nearNormal[0]*invR, nearNormal[1]*invR, nearNormal[2]*invR, nearD*invR };
+ invR = 1.0f/(float)sqrt(-nearNormal[0]*-nearNormal[0]+-nearNormal[1]*-nearNormal[1]+-nearNormal[2]*-nearNormal[2]);
+ float farPlane[4] = { -nearNormal[0]*invR, -nearNormal[1]*invR, -nearNormal[2]*invR, farD*invR };
+
+ for (int i = 0; i < m_uPatches; i++)
+ for (int j = 0; j < m_vPatches; j++)
+ {
+ m_Patches[i][j].visible = true;
+ if (boxIsOutside(m_Patches[i][j].box, leftPlane))
+ {
+ m_Patches[i][j].visible = false;
+ continue;
+ }
+ if (boxIsOutside(m_Patches[i][j].box, rightPlane))
+ {
+ m_Patches[i][j].visible = false;
+ continue;
+ }
+ if (boxIsOutside(m_Patches[i][j].box, nearPlane))
+ {
+ m_Patches[i][j].visible = false;
+ continue;
+ }
+ if (boxIsOutside(m_Patches[i][j].box, farPlane))
+ {
+ m_Patches[i][j].visible = false;
+ continue;
+ }
+ if (boxIsOutside(m_Patches[i][j].box, bottomPlane))
+ {
+ m_Patches[i][j].visible = false;
+ continue;
+ }
+ if (boxIsOutside(m_Patches[i][j].box, topPlane))
+ {
+ m_Patches[i][j].visible = false;
+ continue;
+ }
+ }
+}
+
+void Terrain::LoadDefaults(bool bLinear)
+{
+ unsigned long rgb = SystemGetProfileInt("Default", "Floor", RGB (0,191,0));
+ m_fColor[0] = (float)((unsigned char) (rgb))/255;
+ m_fColor[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
+ m_fColor[2] = (float)((unsigned char) ((rgb) >> 16))/255;
+
+ m_uSize = 50;
+ m_vSize = 50;
+
+ strcpy(m_strTexture, SystemGetProfileString("Default", "FloorBMP", ""));
+ m_pTexture->Unload();
+
+ m_nOptions = LC_TERRAIN_FLAT;
+ if (strlen(m_strTexture))
+ {
+ m_nOptions |= LC_TERRAIN_TEXTURE;
+ LoadTexture(bLinear);
+ }
+
+ SetPatchCount(4, 4);
+
+ for (int i = 0; i < 13; i++)
+ for (int j = 0; j < 13; j++)
+ {
+ m_pControl[i][j*3] = m_uSize * ((float)i/12-0.5f);
+ m_pControl[i][j*3+1] = m_vSize * ((float)j/12-0.5f);
+ m_pControl[i][j*3+2] = 0;
+ }
+
+ SetControlPoints();
+ Tesselate();
+}
+
+void Terrain::LoadTexture(bool bLinear)
+{
+ m_pTexture->Unload();
+
+ if ((m_nOptions & LC_TERRAIN_TEXTURE) == 0)
+ return;
+
+ if (m_pTexture->LoadFromFile(m_strTexture, bLinear) == false)
+ {
+// AfxMessageBox("Could not load terrain texture.", MB_OK|MB_ICONEXCLAMATION);
+ m_nOptions &= ~LC_TERRAIN_TEXTURE;
+ }
+}
+
+void Terrain::GenerateRandom()
+{
+ srand((unsigned)time(NULL));
+
+ int uCount = (m_uPatches*3)+1, vCount = (m_vPatches*3)+1;
+
+ for (int i = 0; i < uCount; i++)
+ for (int j = 0; j < vCount; j++)
+ {
+ m_pControl[i][j*3] = m_uSize * ((float)i/(uCount-1)-0.5f);
+ m_pControl[i][j*3+1] = m_vSize * ((float)j/(vCount-1)-0.5f);
+ m_pControl[i][j*3+2] = (((float)rand()/(float)RAND_MAX) - 0.5f) * 8;
+ }
+
+ SetControlPoints();
+ Tesselate();
+}
diff --git a/common/terrain.h b/common/terrain.h
new file mode 100644
index 0000000..9c41e29
--- /dev/null
+++ b/common/terrain.h
@@ -0,0 +1,92 @@
+//
+// terrain.h
+////////////////////////////////////////////////////
+
+#ifndef _TERRAIN_H_
+#define _TERRAIN_H_
+
+#include "defines.h"
+
+typedef struct {
+ float corners[8][3];
+ float minX, minY, minZ;
+ float maxX, maxY, maxZ;
+
+ void InitBox(float minX, float maxX, float minY, float maxY, float minZ, float maxZ);
+} PATCHBOX;
+
+typedef struct PATCH
+{
+ PATCH()
+ {
+ vertex = NULL;
+ normals = NULL;
+ coords = NULL;
+ index = NULL;
+ steps = 10;
+ visible = true;
+ };
+
+ float control[48]; // 4x4 grid
+ unsigned short steps;
+
+ float* vertex;
+ float* normals;
+ float* coords;
+ unsigned short* index;
+
+ PATCHBOX box;
+ bool visible;
+ void Tesselate(bool bNormals);
+ void FreeMemory();
+} PATCH;
+
+class File;
+class Camera;
+class Texture;
+
+class Terrain
+{
+public:
+ Terrain();
+ ~Terrain();
+ Terrain& operator=(const Terrain& source);
+
+ void LoadTexture(bool bLinear);
+ void Render(Camera* pCam, float aspect);
+ void LoadDefaults(bool bLinear);
+ void SetSize(float uSize, float vSize);
+ void GetSize(float *uSize, float *vSize);
+ void FileLoad(File* file);
+ void FileSave(File* file);
+ void Tesselate();
+ void SetControlPoints();
+ void GenerateRandom();
+
+ void SetPatchCount(int uPatches, int vPatches);
+ void GetPatchCount(int *uCount, int *vCount);
+ int GetCountU()
+ { return m_uPatches != 0 ? m_uPatches*3 + 1 : 0; }
+ int GetCountV()
+ { return m_vPatches != 0 ? m_vPatches*3 + 1 : 0; }
+ float** GetControlPoints()
+ { return m_pControl; }
+
+ unsigned long m_nOptions;
+ char m_strTexture[LC_MAXPATH];
+ float m_fColor[3];
+
+protected:
+ void FreeMemory();
+ void FindVisiblePatches(Camera* pCam, float aspect);
+
+ float** m_pControl;
+ PATCH** m_Patches;
+ int m_uPatches;
+ int m_vPatches;
+ float m_uSize;
+ float m_vSize;
+ Texture* m_pTexture;
+};
+
+#endif // _TERRAIN_H_
diff --git a/common/texture.cpp b/common/texture.cpp
new file mode 100644
index 0000000..febaf9e
--- /dev/null
+++ b/common/texture.cpp
@@ -0,0 +1,183 @@
+// Texture object.
+//
+
+#ifdef _WINDOWS
+#include "stdafx.h"
+#else
+#include "GL/glu.h"
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include "file.h"
+#include "texture.h"
+#include "project.h"
+#include "globals.h"
+#include "image.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// Texture construction/destruction
+
+// Only called for the background image, use LoadIndex()
+Texture::Texture()
+{
+ m_nRef = 1;
+ m_nID = 0;
+}
+
+Texture::~Texture()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Texture attributes
+
+void Texture::AddRef(bool bFilter)
+{
+ if (m_nRef == 0)
+ Load(bFilter);
+
+ m_nRef++;
+}
+
+void Texture::DeRef()
+{
+ m_nRef--;
+ if (m_nRef == 0)
+ Unload();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Load methods
+
+void Texture::LoadIndex(File* idx)
+{
+ unsigned char bt;
+
+ // TODO: don't change ref. if reloading
+ m_nRef = 0;
+ m_nID = 0;
+
+ idx->Read(m_strName, 8);
+ idx->Read(&m_nWidth, sizeof(m_nWidth));
+ idx->Read(&m_nHeight, sizeof(m_nHeight));
+ idx->Read(&bt, sizeof(bt));
+
+ switch (bt)
+ {
+ case LC_INTENSITY: m_nType = GL_LUMINANCE; break;
+ case LC_RGB: m_nType = GL_RGB; break;
+ case LC_RGBA: m_nType = GL_RGBA; break;
+ }
+
+ idx->Read(&m_nOffset, sizeof(m_nOffset));
+}
+
+void Texture::Unload()
+{
+ if (m_nID != 0)
+ glDeleteTextures(1, &m_nID);
+ m_nID = 0;
+}
+
+// Load from textures.bin file
+void Texture::Load(bool bFilter)
+{
+ unsigned char* bits;
+ char filename[LC_MAXPATH];
+ FILE* bin;
+ int size;
+
+ strcpy(filename, project->GetLibraryPath());
+ strcat(filename, "textures.bin");
+ bin = fopen(filename, "rb");
+ if (bin == NULL)
+ return;
+
+ size = m_nWidth*m_nHeight;
+ if (m_nType == GL_RGB)
+ size *= 3;
+ if (m_nType == GL_RGBA)
+ size *= 4;
+ bits = (unsigned char*)malloc(size);
+
+ fseek(bin, m_nOffset, SEEK_SET);
+ fread(bits, 1, size, bin);
+ fclose(bin);
+
+ if (m_nID == 0)
+ glGenTextures(1, &m_nID);
+
+ glBindTexture(GL_TEXTURE_2D, m_nID);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+// glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ if (m_nType == GL_LUMINANCE)
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_INTENSITY4, m_nWidth, m_nHeight,
+ 0, m_nType, GL_UNSIGNED_BYTE, bits);
+ }
+ else
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, bFilter ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, bFilter ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, m_nWidth, m_nHeight,
+ 0, m_nType, GL_UNSIGNED_BYTE, bits);
+
+ if (bFilter)
+ gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB5_A1,
+ m_nWidth, m_nHeight, m_nType, GL_UNSIGNED_BYTE, bits);
+ }
+
+ free(bits);
+}
+
+bool Texture::LoadFromFile(char* strFilename, bool bFilter)
+{
+ LC_IMAGE* image = OpenImage(strFilename);
+
+ if (image == NULL)
+ {
+ if (m_nID != 0)
+ {
+ glDeleteTextures(1, &m_nID);
+ m_nID = 0;
+ }
+ m_nWidth = 0;
+ m_nHeight = 0;
+
+ return false;
+ }
+
+ m_nWidth = image->width;
+ m_nHeight = image->height;
+ m_nType = GL_RGB;
+
+ if (m_nID == 0)
+ glGenTextures(1, &m_nID);
+
+ glBindTexture(GL_TEXTURE_2D, m_nID);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, bFilter ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, bFilter ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexImage2D(GL_TEXTURE_2D, 0, 3, m_nWidth, m_nHeight,
+ 0, m_nType, GL_UNSIGNED_BYTE, image->bits);
+
+ if (bFilter)
+ gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB,
+ m_nWidth, m_nHeight, m_nType, GL_UNSIGNED_BYTE, image->bits);
+
+ free(image);
+
+ return true;
+}
diff --git a/common/texture.h b/common/texture.h
new file mode 100644
index 0000000..546c7c8
--- /dev/null
+++ b/common/texture.h
@@ -0,0 +1,51 @@
+//
+// texture.h
+////////////////////////////////////////////////////
+
+#ifndef _TEXTURE_H
+#define _TEXTURE_H
+
+#ifndef GLuint
+#include <GL/gl.h>
+#endif
+
+class File;
+
+typedef enum { LC_INTENSITY, LC_RGB, LC_RGBA } LC_TEXTURE_TYPES;
+
+class Texture
+{
+public:
+ Texture();
+ ~Texture();
+
+ void MakeCurrent()
+ {
+ if (m_nID != 0)
+ { glBindTexture(GL_TEXTURE_2D, m_nID); }
+ }
+
+ bool IsLoaded()
+ { return glIsTexture(m_nID) == GL_TRUE; }
+ void Load(bool bFilter);
+ bool LoadFromFile(char* strFilename, bool bFilter);
+ void Unload();
+
+ void LoadIndex(File* idx);
+ void AddRef(bool bFilter);
+ void DeRef();
+
+ // Read-only
+ char m_strName[9];
+ unsigned short m_nWidth;
+ unsigned short m_nHeight;
+
+protected:
+ int m_nRef;
+ GLuint m_nID;
+ GLenum m_nType;
+ unsigned long m_nOffset;
+};
+
+
+#endif // _TEXTURE_H
diff --git a/common/tr.cpp b/common/tr.cpp
new file mode 100644
index 0000000..c39087f
--- /dev/null
+++ b/common/tr.cpp
@@ -0,0 +1,325 @@
+// TR.cpp: implementation of the TiledRender class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#ifdef _WINDOWS
+#include "stdafx.h"
+#else
+#include <GL/gl.h>
+#include <GL/glu.h>
+#endif
+#include "tr.h"
+#include <math.h>
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+TiledRender::TiledRender()
+{
+ m_TileWidth = 256;
+ m_TileHeight = 256;
+ m_TileBorder = 0;
+ m_RowOrder = TR_BOTTOM_TO_TOP;
+ m_CurrentTile = -1;
+ m_ImageBuffer = 0;
+ m_TileBuffer = 0;
+}
+
+TiledRender::~TiledRender()
+{
+}
+
+void TiledRender::TileSize(int width, int height, int border)
+{
+ m_TileBorder = border;
+ m_TileWidth = width;
+ m_TileHeight = height;
+ m_TileWidthNB = width - 2 * border;
+ m_TileHeightNB = height - 2 * border;
+}
+
+void TiledRender::TileBuffer(TRenum format, TRenum type, void *image)
+{
+ m_TileFormat = format;
+ m_TileType = type;
+ m_TileBuffer = image;
+}
+
+void TiledRender::ImageSize(int width, int height)
+{
+ m_ImageWidth = width;
+ m_ImageHeight = height;
+}
+
+void TiledRender::ImageBuffer(TRenum format, TRenum type, void *image)
+{
+ m_ImageFormat = format;
+ m_ImageType = type;
+ m_ImageBuffer = image;
+}
+
+void TiledRender::RowOrder(TRenum order)
+{
+ if (order == TR_TOP_TO_BOTTOM || order == TR_BOTTOM_TO_TOP)
+ m_RowOrder = order;
+}
+
+int TiledRender::Get(TRenum param)
+{
+ switch (param)
+ {
+ case TR_TILE_WIDTH:
+ return m_TileWidth;
+ case TR_TILE_HEIGHT:
+ return m_TileHeight;
+ case TR_TILE_BORDER:
+ return m_TileBorder;
+ case TR_IMAGE_WIDTH:
+ return m_ImageWidth;
+ case TR_IMAGE_HEIGHT:
+ return m_ImageHeight;
+ case TR_ROWS:
+ return m_Rows;
+ case TR_COLUMNS:
+ return m_Columns;
+ case TR_CURRENT_ROW:
+ if (m_CurrentTile < 0)
+ return -1;
+ else
+ return m_CurrentRow;
+ case TR_CURRENT_COLUMN:
+ if (m_CurrentTile < 0)
+ return -1;
+ else
+ return m_CurrentColumn;
+ case TR_CURRENT_TILE_WIDTH:
+ return m_CurrentTileWidth;
+ case TR_CURRENT_TILE_HEIGHT:
+ return m_CurrentTileHeight;
+ case TR_ROW_ORDER:
+ return (int) m_RowOrder;
+ default:
+ return 0;
+ }
+}
+
+void TiledRender::Ortho(double left, double right, double bottom, double top, double zNear, double zFar)
+{
+ if (m_CurrentTile < 0)
+ {
+ m_Perspective = false;
+ m_Left = left;
+ m_Right = right;
+ m_Bottom = bottom;
+ m_Top = top;
+ m_Near = zNear;
+ m_Far = zFar;
+ }
+}
+
+void TiledRender::Frustum(double left, double right, double bottom, double top, double zNear, double zFar)
+{
+ if (m_CurrentTile < 0)
+ {
+ m_Perspective = true;
+ m_Left = left;
+ m_Right = right;
+ m_Bottom = bottom;
+ m_Top = top;
+ m_Near = zNear;
+ m_Far = zFar;
+ }
+}
+
+void TiledRender::Perspective(double fovy, double aspect, double zNear, double zFar )
+{
+ double xmin, xmax, ymin, ymax;
+ ymax = zNear * tan(fovy * 3.14159265 / 360.0);
+ ymin = -ymax;
+ xmin = ymin * aspect;
+ xmax = ymax * aspect;
+ Frustum(xmin, xmax, ymin, ymax, zNear, zFar);
+}
+
+void TiledRender::BeginTile()
+{
+ int matrixMode;
+ int tileWidth, tileHeight, tileWidthNB, tileHeightNB, border;
+ double left, right, bottom, top;
+
+ if (m_CurrentTile <= 0)
+ {
+ m_Columns = (m_ImageWidth + m_TileWidthNB - 1) / m_TileWidthNB;
+ m_Rows = (m_ImageHeight + m_TileHeightNB - 1) / m_TileHeightNB;
+ m_CurrentTile = 0;
+
+ // Save user's viewport, will be restored after last tile rendered
+ glGetIntegerv(GL_VIEWPORT, m_ViewportSave);
+ }
+
+ /* which tile (by row and column) we're about to render */
+ if (m_RowOrder == TR_BOTTOM_TO_TOP)
+ {
+ m_CurrentRow = m_CurrentTile / m_Columns;
+ m_CurrentColumn = m_CurrentTile % m_Columns;
+ }
+ else if (m_RowOrder==TR_TOP_TO_BOTTOM)
+ {
+ m_CurrentRow = m_Rows - (m_CurrentTile / m_Columns) - 1;
+ m_CurrentColumn = m_CurrentTile % m_Columns;
+ }
+
+ border = m_TileBorder;
+
+ /* Compute actual size of this tile with border */
+ if (m_CurrentRow < m_Rows-1)
+ tileHeight = m_TileHeight;
+ else
+ tileHeight = m_ImageHeight - (m_Rows-1) * (m_TileHeightNB) + 2 * border;
+
+ if (m_CurrentColumn < m_Columns-1)
+ tileWidth = m_TileWidth;
+ else
+ tileWidth = m_ImageWidth - (m_Columns-1) * (m_TileWidthNB) + 2 * border;
+
+ /* tile size with No Border */
+ tileWidthNB = tileWidth - 2 * border;
+ tileHeightNB = tileHeight - 2 * border;
+
+ /* Save tile size, with border */
+ m_CurrentTileWidth = tileWidth;
+ m_CurrentTileHeight = tileHeight;
+
+ glViewport(0, 0, tileWidth, tileHeight); /* tile size including border */
+
+ /* save current matrix mode */
+ glGetIntegerv(GL_MATRIX_MODE, &matrixMode);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+
+ /* compute projection parameters */
+ left = m_Left + (m_Right - m_Left)
+ * (m_CurrentColumn * m_TileWidthNB - border) / m_ImageWidth;
+ right = left + (m_Right - m_Left) * tileWidth / m_ImageWidth;
+ bottom = m_Bottom + (m_Top - m_Bottom)
+ * (m_CurrentRow * m_TileHeightNB - border) / m_ImageHeight;
+ top = bottom + (m_Top - m_Bottom) * tileHeight / m_ImageHeight;
+
+ if (m_Perspective)
+ glFrustum(left, right, bottom, top, m_Near, m_Far);
+ else
+ glOrtho(left, right, bottom, top, m_Near, m_Far);
+
+ /* restore user's matrix mode */
+ glMatrixMode((GLenum)matrixMode);
+}
+
+int TiledRender::EndTile()
+{
+ int prevRowLength, prevSkipRows, prevSkipPixels;
+
+ // be sure OpenGL rendering is finished
+ glFlush();
+
+ // save current glPixelStore values
+ glGetIntegerv(GL_PACK_ROW_LENGTH, &prevRowLength);
+ glGetIntegerv(GL_PACK_SKIP_ROWS, &prevSkipRows);
+ glGetIntegerv(GL_PACK_SKIP_PIXELS, &prevSkipPixels);
+
+ if (m_TileBuffer)
+ {
+ int srcX = m_TileBorder;
+ int srcY = m_TileBorder;
+ int srcWidth = m_TileWidthNB;
+ int srcHeight = m_TileHeightNB;
+ glReadPixels(srcX, srcY, srcWidth, srcHeight,
+ (GLenum)m_TileFormat, (GLenum)m_TileType, m_TileBuffer);
+ }
+
+ if (m_ImageBuffer)
+ {
+ int srcX = m_TileBorder;
+ int srcY = m_TileBorder;
+ int srcWidth = m_CurrentTileWidth - 2 * m_TileBorder;
+ int srcHeight = m_CurrentTileHeight - 2 * m_TileBorder;
+ int destX = m_TileWidthNB * m_CurrentColumn;
+ int destY = m_TileHeightNB * m_CurrentRow;
+
+ // setup pixel store for glReadPixels
+ glPixelStorei(GL_PACK_ROW_LENGTH, m_ImageWidth);
+ glPixelStorei(GL_PACK_SKIP_ROWS, destY);
+ glPixelStorei(GL_PACK_SKIP_PIXELS, destX);
+
+ // read the tile into the final image
+ glReadPixels(srcX, srcY, srcWidth, srcHeight,
+ (GLenum)m_ImageFormat, (GLenum)m_ImageType, m_ImageBuffer);
+ }
+
+ // restore previous glPixelStore values
+ glPixelStorei(GL_PACK_ROW_LENGTH, prevRowLength);
+ glPixelStorei(GL_PACK_SKIP_ROWS, prevSkipRows);
+ glPixelStorei(GL_PACK_SKIP_PIXELS, prevSkipPixels);
+
+ // increment tile counter, return 1 if more tiles left to render
+ m_CurrentTile++;
+ if (m_CurrentTile >= m_Rows * m_Columns)
+ {
+ // restore user's viewport
+ glViewport(m_ViewportSave[0], m_ViewportSave[1],
+ m_ViewportSave[2], m_ViewportSave[3]);
+ m_CurrentTile = -1; // all done
+ return 0;
+ }
+ else
+ return 1;
+}
+
+void TiledRender::RasterPos3f(float x, float y, float z)
+{
+ if (m_CurrentTile < 0)
+ {
+ // not doing tile rendering right now. Let OpenGL do this.
+ glRasterPos3f(x, y, z);
+ }
+ else
+ {
+ double modelview[16], proj[16];
+ int viewport[4];
+ double winX, winY, winZ;
+
+ // Get modelview, projection and viewport
+ glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
+ glGetDoublev(GL_PROJECTION_MATRIX, proj);
+ viewport[0] = 0;
+ viewport[1] = 0;
+ viewport[2] = m_CurrentTileWidth;
+ viewport[3] = m_CurrentTileHeight;
+
+ // Project object coord to window coordinate
+ if (gluProject(x, y, z, modelview, proj, viewport, &winX, &winY, &winZ))
+ {
+ // set raster pos to window coord (0,0)
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glOrtho(0.0, m_CurrentTileWidth,
+ 0.0, m_CurrentTileHeight, 0.0, 1.0);
+ glRasterPos3f(0.0, 0.0, (float)-winZ);
+
+ // Now use empty bitmap to adjust raster position to (winX,winY)
+ {
+ GLubyte bitmap[1] = {0};
+ glBitmap(1, 1, 0.0, 0.0, (float)winX, (float)winY, bitmap);
+ }
+
+ // restore original matrices
+ glPopMatrix(); // proj
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ }
+ }
+}
diff --git a/common/tr.h b/common/tr.h
new file mode 100644
index 0000000..024adaf
--- /dev/null
+++ b/common/tr.h
@@ -0,0 +1,75 @@
+// TR.h: interface for the TiledRender class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#ifndef _TR_H_
+#define _TR_H_
+
+typedef enum {
+ TR_TILE_WIDTH = 100,
+ TR_TILE_HEIGHT,
+ TR_TILE_BORDER,
+ TR_IMAGE_WIDTH,
+ TR_IMAGE_HEIGHT,
+ TR_ROWS,
+ TR_COLUMNS,
+ TR_CURRENT_ROW,
+ TR_CURRENT_COLUMN,
+ TR_CURRENT_TILE_WIDTH,
+ TR_CURRENT_TILE_HEIGHT,
+ TR_ROW_ORDER,
+ TR_TOP_TO_BOTTOM,
+ TR_BOTTOM_TO_TOP
+} TRenum;
+
+class TiledRender
+{
+public:
+ TiledRender();
+ virtual ~TiledRender();
+
+ void TileSize(int width, int height, int border);
+ void TileBuffer(TRenum format, TRenum type, void *image);
+ void ImageSize(int width, int height);
+ void ImageBuffer(TRenum format, TRenum type, void *image);
+ void RowOrder(TRenum order);
+ void Ortho(double left, double right, double bottom, double top, double zNear, double zFar);
+ void Frustum(double left, double right, double bottom, double top, double zNear, double zFar);
+ void Perspective(double fovy, double aspect, double zNear, double zFar );
+ void RasterPos3f(float x, float y, float z);
+ int Get(TRenum param);
+ int EndTile();
+ void BeginTile();
+
+ // Final image parameters
+ int m_ImageWidth, m_ImageHeight;
+ TRenum m_ImageFormat, m_ImageType;
+ void *m_ImageBuffer;
+
+ // Tile parameters
+ int m_TileWidth, m_TileHeight;
+ int m_TileWidthNB, m_TileHeightNB;
+ int m_TileBorder;
+ TRenum m_TileFormat, m_TileType;
+ void *m_TileBuffer;
+
+ // Projection parameters
+ bool m_Perspective;
+ double m_Left;
+ double m_Right;
+ double m_Bottom;
+ double m_Top;
+ double m_Near;
+ double m_Far;
+
+ // Misc
+ TRenum m_RowOrder;
+ int m_Rows, m_Columns;
+ int m_CurrentTile;
+ int m_CurrentTileWidth, m_CurrentTileHeight;
+ int m_CurrentRow, m_CurrentColumn;
+
+ int m_ViewportSave[4];
+};
+
+#endif // _TR_H_
diff --git a/common/typedefs.h b/common/typedefs.h
new file mode 100644
index 0000000..4588652
--- /dev/null
+++ b/common/typedefs.h
@@ -0,0 +1,318 @@
+// Typedefs.
+//
+
+#ifndef _TYPEDEF_H_
+#define _TYPEDEF_H_
+
+class Group;
+class Piece;
+class PieceInfo;
+#include "defines.h"
+
+typedef enum {
+ LC_COLOR_CHANGED,
+ LC_GROUP_CHANGED,
+ LC_CAPTURE_LOST,
+ LC_ACTIVATE,
+ LC_PIECE_MODIFIED,
+ LC_CAMERA_MODIFIED,
+ LC_LIGHT_MODIFIED
+} LC_NOTIFY;
+
+typedef enum {
+ LC_FILE_NEW,
+ LC_FILE_OPEN,
+ LC_FILE_MERGE,
+ LC_FILE_SAVE,
+ LC_FILE_SAVEAS,
+ LC_FILE_PICTURE,
+ LC_FILE_3DS,
+ LC_FILE_HTML,
+ LC_FILE_POVRAY,
+ LC_FILE_WAVEFRONT,
+ LC_FILE_PROPERTIES,
+ LC_FILE_TERRAIN,
+ LC_FILE_LIBRARY,
+ LC_FILE_RECENT,
+ LC_EDIT_UNDO,
+ LC_EDIT_REDO,
+ LC_EDIT_CUT,
+ LC_EDIT_COPY,
+ LC_EDIT_PASTE,
+ LC_EDIT_SELECT_ALL,
+ LC_EDIT_SELECT_NONE,
+ LC_EDIT_SELECT_INVERT,
+ LC_EDIT_SELECT_BYNAME,
+ LC_PIECE_INSERT,
+ LC_PIECE_DELETE,
+ LC_PIECE_MINIFIG,
+ LC_PIECE_ARRAY,
+ LC_PIECE_COPYKEYS,
+ LC_PIECE_GROUP,
+ LC_PIECE_UNGROUP,
+ LC_PIECE_GROUP_ADD,
+ LC_PIECE_GROUP_REMOVE,
+ LC_PIECE_GROUP_EDIT,
+ LC_PIECE_HIDE_SELECTED,
+ LC_PIECE_HIDE_UNSELECTED,
+ LC_PIECE_UNHIDE_ALL,
+ LC_PIECE_PREVIOUS,
+ LC_PIECE_NEXT,
+ LC_VIEW_PREFERENCES,
+ LC_VIEW_ZOOMIN,
+ LC_VIEW_ZOOMOUT,
+ LC_VIEW_ZOOMEXTENTS,
+ LC_VIEW_VIEWPORTS,
+ LC_VIEW_STEP_NEXT,
+ LC_VIEW_STEP_PREVIOUS,
+ LC_VIEW_STEP_FIRST,
+ LC_VIEW_STEP_LAST,
+ LC_VIEW_STEP_CHOOSE,
+ LC_VIEW_STEP_SET,
+ LC_VIEW_STOP,
+ LC_VIEW_PLAY,
+ LC_VIEW_CAMERA_MENU,
+ LC_VIEW_CAMERA_RESET,
+ LC_VIEW_AUTOPAN,
+ LC_HELP_ABOUT,
+ LC_TOOLBAR_ANIMATION,
+ LC_TOOLBAR_ADDKEYS,
+ LC_TOOLBAR_SNAPMENU,
+ LC_TOOLBAR_LOCKMENU,
+ LC_TOOLBAR_SNAPMOVEMENU,
+ LC_TOOLBAR_FASTRENDER,
+ LC_TOOLBAR_BACKGROUND
+} LC_COMMANDS;
+
+typedef enum {
+ LC_ACTION_SELECT = 0,
+ LC_ACTION_INSERT,
+ LC_ACTION_LIGHT,
+ LC_ACTION_SPOTLIGHT,
+ LC_ACTION_CAMERA,
+ LC_ACTION_MOVE,
+ LC_ACTION_ROTATE,
+ LC_ACTION_ERASER,
+ LC_ACTION_PAINT,
+ LC_ACTION_ZOOM,
+ LC_ACTION_ZOOM_REGION,
+ LC_ACTION_PAN,
+ LC_ACTION_ROTATE_VIEW,
+ LC_ACTION_ROLL
+} LC_ACTIONS;
+
+// Piece connections (complicated and wastes memory but fast).
+
+typedef struct CONNECTION
+{
+ unsigned char type;
+ float center[3];
+ float normal[3];
+ CONNECTION* link;
+ Piece* owner;
+} CONNECTION;
+
+typedef struct
+{
+ Piece* owner;
+ CONNECTION** cons; // pointers to the structures in each piece
+ unsigned short numcons;
+} CONNECTION_ENTRY;
+
+typedef struct
+{
+ CONNECTION_ENTRY* entries;
+ unsigned short numentries;
+} CONNECTION_TYPE;
+
+
+// Select by Name dialog data
+
+typedef enum
+{
+ LC_SELDLG_PIECE,
+ LC_SELDLG_CAMERA,
+ LC_SELDLG_LIGHT,
+ LC_SELDLG_GROUP
+} LC_SEL_DATA_TYPE;
+
+typedef struct
+{
+ const char* name;
+ unsigned char type;
+ bool selected;
+ void* pointer;
+} LC_SEL_DATA;
+
+typedef struct
+{
+ void* piece;
+ char name[81];
+ float pos[3];
+ float rot[3];
+ int from;
+ int to;
+ bool hidden;
+ int color;
+} LC_PIECE_MODIFY;
+
+typedef struct
+{
+ void* camera;
+ char name[81];
+ float eye[3];
+ float target[3];
+ float up[3];
+ float fovy;
+ float znear;
+ float zfar;
+ bool hidden;
+} LC_CAMERA_MODIFY;
+
+// Image
+
+typedef enum {
+ LC_IMAGE_BMP,
+ LC_IMAGE_GIF,
+ LC_IMAGE_JPG,
+ LC_IMAGE_AVI
+} LC_IMAGE_FORMATS;
+
+
+typedef struct
+{
+ unsigned short width;
+ unsigned short height;
+ void* bits;
+} LC_IMAGE;
+
+typedef struct
+{
+ unsigned char quality;
+ bool interlaced;
+ bool transparent;
+ bool truecolor;
+ unsigned char background[3];
+ float pause;
+ unsigned char format;
+} LC_IMAGE_OPTS;
+
+typedef struct
+{
+ char filename[LC_MAXPATH];
+ unsigned short from;
+ unsigned short to;
+ bool multiple;
+ unsigned short width;
+ unsigned short height;
+ LC_IMAGE_OPTS imopts;
+} LC_IMAGEDLG_OPTS;
+
+typedef enum {
+ LC_DLG_FILE_OPEN,
+ LC_DLG_FILE_SAVE,
+ LC_DLG_FILE_MERGE,
+ LC_DLG_PICTURE_SAVE,
+ LC_DLG_HTML,
+ LC_DLG_POVRAY,
+ LC_DLG_WAVEFRONT,
+ LC_DLG_MINIFIG,
+ LC_DLG_ARRAY,
+ LC_DLG_PREFERENCES,
+ LC_DLG_PROPERTIES,
+ LC_DLG_TERRAIN,
+ LC_DLG_LIBRARY,
+ LC_DLG_SELECTBYNAME,
+ LC_DLG_STEPCHOOSE,
+ LC_DLG_EDITGROUPS,
+ LC_DLG_GROUP,
+ LC_DLG_ABOUT
+} LC_DIALOGS;
+
+typedef struct
+{
+ bool render;
+ char povpath[LC_MAXPATH];
+ char outpath[LC_MAXPATH];
+ char libpath[LC_MAXPATH];
+} LC_POVRAYDLG_OPTS;
+
+typedef struct
+{
+ char path[LC_MAXPATH];
+ bool singlepage;
+ bool index;
+ bool images;
+ bool listend;
+ bool liststep;
+ bool hilite;
+ LC_IMAGEDLG_OPTS imdlg;
+} LC_HTMLDLG_OPTS;
+
+// hat, head, torso, neck, arml, armr, handl, handr, tooll,
+// toolr, hips, legl, legr, shoel, shoer
+typedef struct
+{
+ PieceInfo* info[15];
+ int colors[15];
+ float pos[15][3];
+ float rot[15][3];
+} LC_MINIFIGDLG_OPTS;
+
+typedef struct
+{
+ unsigned short n1DCount;
+ unsigned short n2DCount;
+ unsigned short n3DCount;
+ unsigned char nArrayDimension;
+ float f2D[3];
+ float f3D[3];
+ float fMove[3];
+ float fRotate[3];
+} LC_ARRAYDLG_OPTS;
+
+typedef struct
+{
+ char strAuthor[101];
+ char strDescription[101];
+ char strComments[256];
+ char* strTitle;
+ char* strFilename;
+ char** names;
+ unsigned short* count;
+ int lines;
+} LC_PROPERTIESDLG_OPTS;
+
+typedef struct
+{
+ int piececount;
+ Piece** pieces;
+ Group** piecesgroups;
+ int groupcount;
+ Group** groups;
+ Group** groupsgroups;
+} LC_GROUPEDITDLG_OPTS;
+
+typedef struct
+{
+ int nMouse;
+ int nSaveInterval;
+ char strPath[LC_MAXPATH];
+ unsigned long nDetail;
+ float fLineWidth;
+ unsigned long nSnap;
+ unsigned short nAngleSnap;
+ unsigned short nGridSize;
+ unsigned long nScene;
+ float fDensity;
+ char strBackground[LC_MAXPATH];
+ float fBackground[4];
+ float fFog[4];
+ float fAmbient[4];
+ float fGrad1[3];
+ float fGrad2[3];
+ char strFooter[256];
+ char strHeader[256];
+} LC_PREFERENCESDLG_OPTS;
+
+#endif
diff --git a/common/vector.cpp b/common/vector.cpp
new file mode 100644
index 0000000..6a61fc5
--- /dev/null
+++ b/common/vector.cpp
@@ -0,0 +1,178 @@
+// Vector.cpp: implementation of the Vector class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include <math.h>
+#include "vector.h"
+#include "defines.h"
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+
+Vector::Vector(float x, float y, float z)
+{
+ m_fPoint[0] = x;
+ m_fPoint[1] = y;
+ m_fPoint[2] = z;
+}
+
+Vector::Vector(float point[3])
+{
+ m_fPoint[0] = point[0];
+ m_fPoint[1] = point[1];
+ m_fPoint[2] = point[2];
+}
+
+Vector::Vector(float p1[3], float p2[3])
+{
+ m_fPoint[0] = p2[0]-p1[0];
+ m_fPoint[1] = p2[1]-p1[1];
+ m_fPoint[2] = p2[2]-p1[2];
+}
+
+Vector::Vector()
+{
+
+}
+
+Vector::~Vector()
+{
+
+}
+
+//////////////////////////////////////////////////////////////////////
+// Operations
+
+float Vector::X()
+{
+ return m_fPoint[0];
+}
+
+float Vector::Y()
+{
+ return m_fPoint[1];
+}
+
+float Vector::Z()
+{
+ return m_fPoint[2];
+}
+
+bool Vector::operator==(Vector& vec)
+{
+ if (m_fPoint[0] != vec.m_fPoint[0])
+ return false;
+ if (m_fPoint[1] != vec.m_fPoint[1])
+ return false;
+ if (m_fPoint[2] != vec.m_fPoint[2])
+ return false;
+ return true;
+}
+
+float Vector::Length()
+{
+ return (float)sqrt(m_fPoint[0]*m_fPoint[0] + m_fPoint[1]*m_fPoint[1] + m_fPoint[2]*m_fPoint[2]);
+}
+
+void Vector::Normalize()
+{
+ float inv = 1.0f/(float)sqrt(m_fPoint[0]*m_fPoint[0] + m_fPoint[1]*m_fPoint[1] + m_fPoint[2]*m_fPoint[2]);
+ m_fPoint[0] *= inv;
+ m_fPoint[1] *= inv;
+ m_fPoint[2] *= inv;
+}
+
+Vector& Vector::operator*=(float scalar)
+{
+ m_fPoint[0] *= scalar;
+ m_fPoint[1] *= scalar;
+ m_fPoint[2] *= scalar;
+ return *this;
+}
+
+void Vector::Add(float point[3])
+{
+ m_fPoint[0] += point[0];
+ m_fPoint[1] += point[1];
+ m_fPoint[2] += point[2];
+}
+
+void Vector::Add(float x, float y, float z)
+{
+ m_fPoint[0] += x;
+ m_fPoint[1] += y;
+ m_fPoint[2] += z;
+}
+
+Vector& Vector::operator+=(Vector& add)
+{
+ m_fPoint[0] += add.m_fPoint[0];
+ m_fPoint[1] += add.m_fPoint[1];
+ m_fPoint[2] += add.m_fPoint[2];
+ return *this;
+}
+
+Vector& Vector::operator=(Vector& source)
+{
+ m_fPoint[0] = source.m_fPoint[0];
+ m_fPoint[1] = source.m_fPoint[1];
+ m_fPoint[2] = source.m_fPoint[2];
+ return *this;
+}
+
+Vector& Vector::operator=(float point[3])
+{
+ m_fPoint[0] = point[0];
+ m_fPoint[1] = point[1];
+ m_fPoint[2] = point[2];
+ return *this;
+}
+
+void Vector::Scale(float scale)
+{
+ m_fPoint[0] *= scale;
+ m_fPoint[1] *= scale;
+ m_fPoint[2] *= scale;
+}
+
+Vector& Vector::Cross(Vector& v1, Vector& v2)
+{
+ m_fPoint[0] = v1.m_fPoint[1]*v2.m_fPoint[2] - v1.m_fPoint[2]*v2.m_fPoint[1];
+ m_fPoint[1] = v1.m_fPoint[2]*v2.m_fPoint[0] - v1.m_fPoint[0]*v2.m_fPoint[2];
+ m_fPoint[2] = v1.m_fPoint[0]*v2.m_fPoint[1] - v1.m_fPoint[1]*v2.m_fPoint[0];
+ return *this;
+}
+
+float Vector::Angle(Vector& vec)
+{
+ return (float)(RTOD * acos ((m_fPoint[0]*vec.m_fPoint[0]+m_fPoint[1]*vec.m_fPoint[1]+m_fPoint[2]*vec.m_fPoint[2])
+ / (sqrt(m_fPoint[0]*m_fPoint[0]+m_fPoint[1]*m_fPoint[1]+m_fPoint[2]*m_fPoint[2])*
+ sqrt(vec.m_fPoint[0]*vec.m_fPoint[0]+vec.m_fPoint[1]*vec.m_fPoint[1]+vec.m_fPoint[2]*vec.m_fPoint[2])) ));
+}
+
+float Vector::Dot(Vector& vec)
+{
+ return m_fPoint[0]*vec.m_fPoint[0]+m_fPoint[1]*vec.m_fPoint[1]+m_fPoint[2]*vec.m_fPoint[2];
+}
+
+void Vector::ToFloat(float point[3])
+{
+ point[0] = m_fPoint[0];
+ point[1] = m_fPoint[1];
+ point[2] = m_fPoint[2];
+}
+
+void Vector::FromFloat(float point[3])
+{
+ m_fPoint[0] = point[0];
+ m_fPoint[1] = point[1];
+ m_fPoint[2] = point[2];
+}
+
+void Vector::FromFloat(float x, float y, float z)
+{
+ m_fPoint[0] = x;
+ m_fPoint[1] = y;
+ m_fPoint[2] = z;
+}
+
diff --git a/common/vector.h b/common/vector.h
new file mode 100644
index 0000000..de613dc
--- /dev/null
+++ b/common/vector.h
@@ -0,0 +1,43 @@
+// Vector.h: interface for the Vector class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#ifndef _VECTOR_H_
+#define _VECTOR_H_
+
+class Vector
+{
+public:
+ float X();
+ float Y();
+ float Z();
+ float Dot(Vector& vec);
+ float Angle(Vector& vec);
+ void Add(float point[3]);
+ void Add(float x, float y, float z);
+ Vector& operator+=(Vector& add);
+ Vector& operator*=(float scalar);
+ Vector& operator=(Vector& source);
+ Vector& operator=(float point[3]);
+ bool operator==(Vector& vec);
+
+ Vector& Cross(Vector& v1, Vector& v2);
+
+ void ToFloat(float point[3]);
+ void FromFloat(float point[3]);
+ void FromFloat(float x, float y, float z);
+ float Length();
+ void Normalize();
+ void Scale(float scale);
+
+ Vector(float x, float y, float z);
+ Vector(float point[3]);
+ Vector(float p1[3], float p2[3]);
+ Vector();
+ virtual ~Vector();
+
+protected:
+ float m_fPoint[3];
+};
+
+#endif // _VECTOR_H_