Copied 0.75 branch as the new trunk.

This commit is contained in:
leo 2011-09-07 21:06:51 +00:00
commit 1f9a1761c0
443 changed files with 94172 additions and 0 deletions

11
CREDITS.txt Normal file
View file

@ -0,0 +1,11 @@
The following people have contributed to LeoCAD:
Author, Original Design:
Leonardo Zide <leo@gerf.org>
Contributions:
Christian Höltje <docwhat@gerf.org>
Renaud Breard <renaud@islande.net>
Linux man page and Debian packages:
Pat Mahoney <pat7@gmx.net>

152
Makefile Normal file
View file

@ -0,0 +1,152 @@
### ALL CONFIGURATION SHOULD BE IN CONFIG.MK, NOT HERE
include config.mk
### Module directories
MODULES := $(OSDIR) common
### look for include files in
### each of the modules
CPPFLAGS += $(patsubst %,-I%,$(MODULES)) $(OS)
CPPFLAGS += -g
### extra libraries if required
LIBS :=
### each module will add to this
SRC :=
BIN := bin/leocad
-include $(OSDIR)/config.mk
### include the description for
### each module
include $(patsubst %,%/module.mk,$(MODULES))
### determine the object files
OBJ := \
$(patsubst %.c,%.o,$(filter %.c,$(SRC))) \
$(patsubst %.cpp,%.o,$(filter %.cpp,$(SRC)))
### link the program
.PHONY: all static
all: $(BIN)
static: bin/leocad.static
bin/leocad: $(OBJ) bin
$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS)
bin/leocad.static: $(OBJ) bin
$(CXX) -static -o $@ $(OBJ) $(LIBS) $(LDFLAGS)
bin:
mkdir bin
### include the C/C++ include
### dependencies
ifeq ($(findstring $(MAKECMDGOALS), help config-help config clean veryclean spotless), )
-include $(OBJ:.o=.d)
endif
### calculate C/C++ include
### dependencies
%.d: %.c
@[ -s $(OSDIR)/config.h ] || $(MAKE) config
@$(CC) -MM -MT '$(patsubst %.d,%.o, $@)' $(CFLAGS) $(CPPFLAGS) -w $< > $@
@[ -s $@ ] || rm -f $@
%.d: %.cpp
@[ -s $(OSDIR)/config.h ] || $(MAKE) config
@$(CXX) -MM -MT '$(patsubst %.d,%.o, $@)' $(CXXFLAGS) $(CPPFLAGS) -w $< > $@
@[ -s $@ ] || rm -f $@
### Various cleaning functions
.PHONY: clean veryclean spotless all
clean:
find $(MODULES) -name \*.o | xargs rm -f
veryclean: clean
find $(MODULES) -name \*.d | xargs rm -f
rm -rf bin
spotless: veryclean
rm -rf arch $(OSDIR)/config.mk $(OSDIR)/config.h
### dependency stuff is done automatically, so these do nothing.
.PHONY: dep depend
### Help function
.PHONY: help
help:
@echo 'Possible Targets are:'
@echo ' help (this is it)'
@echo ' all'
@echo ' install'
@echo ' binary'
@echo ' source'
@echo ' (binary and source can be called as'
@echo ' a -zip or -tgz variants)'
@echo ' clean'
@echo ' veryclean'
@echo ' spotless'
@echo
### Rules to make various packaging
.PHONY: binary binary-tgz source-zip source-tgz source install
arch:
mkdir arch
install: $(BIN)
install -d $(DESTDIR)$(PREFIX)/bin
install -d $(DESTDIR)$(PREFIX)/share/man/man1
install -c -m 0755 $(BIN) $(DESTDIR)$(PREFIX)/bin/
install -c -m 0644 docs/leocad.1 $(DESTDIR)$(PREFIX)/share/man/man1/
binary: binary-zip binary-tgz
binary-zip: arch/leocad-$(VERSION)-linux.zip
binary-tgz: arch/leocad-$(VERSION)-linux.tgz
source: source-tgz source-zip
source-tgz: arch/leocad-$(VERSION)-src.tgz
source-zip: arch/leocad-$(VERSION)-src.zip
### Create a directory with the files needed for a binary package
package-dir: arch all
mkdir leocad-$(VERSION)
cp bin/leocad leocad-$(VERSION)
cp CREDITS.txt leocad-$(VERSION)/CREDITS
cp README.txt leocad-$(VERSION)/README
cp docs/INSTALL.txt leocad-$(VERSION)/INSTALL
cp docs/LINUX.txt leocad-$(VERSION)/LINUX
cp docs/leocad.1 leocad-$(VERSION)
arch/leocad-$(VERSION)-linux.zip: package-dir
rm -f $@
zip -r $@ leocad-$(VERSION)
rm -rf leocad-$(VERSION)
arch/leocad-$(VERSION)-linux.tgz: package-dir
rm -f $@
tar -cvzf $@ leocad-$(VERSION)
rm -rf leocad-$(VERSION)
arch/leocad-$(VERSION)-src.tgz: arch veryclean
rm -f $@
( cd .. ; tar --exclude=leocad/arch/\* --exclude=CVS \
-cvzf leocad/$@ leocad )
arch/leocad-$(VERSION)-src.zip: arch veryclean
rm -f $@
( cd .. ; zip -r leocad/$@ leocad -x '*/arch/*' -x '*/CVS/*' -x '*~' -x '*/core' -x '*/.#*')

61
README.txt Normal file
View file

@ -0,0 +1,61 @@
About LeoCAD
------------
LeoCAD is a CAD program that allows people to build virtual models using
bricks similar to those found in LEGO toys. It's available for free under
the GNU Public License v2, and works on the Windows and Linux Operating
Systems.
Installation
------------
You can download the latest version of LeoCAD and its Pieces Library from
the main website at http://www.leocad.org
It's recommended that you install the latest drivers for your video card,
since LeoCAD can take advantage of hardware acceleration for rendering by
using OpenGL.
- LeoCAD for Windows:
Download LeoCAD-0.75.2-4351.exe to your computer, double click on the
icon to launch the installer and follow the intructions.
- LeoCAD for Linux:
You can either download the source from Subversion or use the i386 rpm
binaries. If you decide to compile it yourself you will also need to
download the Pieces Library separately, extract it to any directory of
your choice and point the LEOCAD_LIB environment variable to it. More
information is available at http://www.leocad.org
New users should take a look at the online tutorial located at
http://www.leocad.org/trac/wiki/BasicTutorial/ to learn how to use LeoCAD.
Online Resources
----------------
- Website:
http://www.leocad.org/
- Mailing lists:
http://gerf.org/mailman/listinfo
- LUGNET Newsgroup:
http://news.lugnet.com/cad/leocad/
news://lugnet.com/lugnet.cad.leocad
- Subversion access:
http://svn.leocad.org/
- Bug database:
http://www.leocad.org/trac/report
Legal Disclaimer
----------------
LEGO is a trademark of the LEGO Group of companies which does not sponsor,
authorize or endorse this software.

1
common/Makefile Normal file
View file

@ -0,0 +1 @@
include ../generic.mk

433
common/algebra.cpp Normal file
View file

@ -0,0 +1,433 @@
//
// Math and Linear Algebra stuff.
//
#include "defines.h"
#include "algebra.h"
// ============================================================================
// 4x4 Matrix class.
void Matrix44::CreateLookAt(const Vector3& Eye, const Vector3& Target, const Vector3& Up)
{
Vector3 x, y, z;
// Z = Eye - Target
z = Eye - Target;
// X = Y Cross Z
x = Cross3(Up, z);
// Y = Z Cross X
y = Cross3(z, x);
// Normalize everything.
x.Normalize();
y.Normalize();
z.Normalize();
m_Rows[0] = Vector4(x[0], y[0], z[0], 0.0f);
m_Rows[1] = Vector4(x[1], y[1], z[1], 0.0f);
m_Rows[2] = Vector4(x[2], y[2], z[2], 0.0f);
m_Rows[3] = m_Rows[0]*-Eye[0] + m_Rows[1]*-Eye[1] + m_Rows[2]*-Eye[2];
m_Rows[3][3] = 1.0f;
}
void Matrix44::CreatePerspective(float FoVy, float Aspect, float Near, float Far)
{
float Left, Right, Bottom, Top;
Top = Near * (float)tan(FoVy * LC_PI / 360.0f);
Bottom = -Top;
Left = Bottom * Aspect;
Right = Top * Aspect;
if ((Near <= 0.0f) || (Far <= 0.0f) || (Near == Far) || (Left == Right) || (Top == Bottom))
return;
float x, y, a, b, c, d;
x = (2.0f * Near) / (Right - Left);
y = (2.0f * Near) / (Top - Bottom);
a = (Right + Left) / (Right - Left);
b = (Top + Bottom) / (Top - Bottom);
c = -(Far + Near) / (Far - Near);
d = -(2.0f * Far * Near) / (Far - Near);
m_Rows[0] = Vector4(x, 0, 0, 0);
m_Rows[1] = Vector4(0, y, 0, 0);
m_Rows[2] = Vector4(a, b, c, -1);
m_Rows[3] = Vector4(0, 0, d, 0);
}
// Inverse code from the GLU library.
Matrix44 Inverse(const Matrix44& m)
{
#define SWAP_ROWS(a, b) { float *_tmp = a; (a)=(b); (b)=_tmp; }
#define MAT(m,c,r) m.m_Rows[r][c]
float wtmp[4][8];
float m0, m1, m2, m3, s;
float *r0, *r1, *r2, *r3;
r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3];
r0[0] = MAT(m,0,0), r0[1] = MAT(m,0,1),
r0[2] = MAT(m,0,2), r0[3] = MAT(m,0,3),
r0[4] = 1.0, r0[5] = r0[6] = r0[7] = 0.0,
r1[0] = MAT(m,1,0), r1[1] = MAT(m,1,1),
r1[2] = MAT(m,1,2), r1[3] = MAT(m,1,3),
r1[5] = 1.0, r1[4] = r1[6] = r1[7] = 0.0,
r2[0] = MAT(m,2,0), r2[1] = MAT(m,2,1),
r2[2] = MAT(m,2,2), r2[3] = MAT(m,2,3),
r2[6] = 1.0, r2[4] = r2[5] = r2[7] = 0.0,
r3[0] = MAT(m,3,0), r3[1] = MAT(m,3,1),
r3[2] = MAT(m,3,2), r3[3] = MAT(m,3,3),
r3[7] = 1.0, r3[4] = r3[5] = r3[6] = 0.0;
// choose pivot - or die
if (fabs(r3[0])>fabs(r2[0])) SWAP_ROWS(r3, r2);
if (fabs(r2[0])>fabs(r1[0])) SWAP_ROWS(r2, r1);
if (fabs(r1[0])>fabs(r0[0])) SWAP_ROWS(r1, r0);
// if (0.0 == r0[0]) return GL_FALSE;
// eliminate first variable
m1 = r1[0]/r0[0]; m2 = r2[0]/r0[0]; m3 = r3[0]/r0[0];
s = r0[1]; r1[1] -= m1 * s; r2[1] -= m2 * s; r3[1] -= m3 * s;
s = r0[2]; r1[2] -= m1 * s; r2[2] -= m2 * s; r3[2] -= m3 * s;
s = r0[3]; r1[3] -= m1 * s; r2[3] -= m2 * s; r3[3] -= m3 * s;
s = r0[4];
if (s != 0.0) { r1[4] -= m1 * s; r2[4] -= m2 * s; r3[4] -= m3 * s; }
s = r0[5];
if (s != 0.0) { r1[5] -= m1 * s; r2[5] -= m2 * s; r3[5] -= m3 * s; }
s = r0[6];
if (s != 0.0) { r1[6] -= m1 * s; r2[6] -= m2 * s; r3[6] -= m3 * s; }
s = r0[7];
if (s != 0.0) { r1[7] -= m1 * s; r2[7] -= m2 * s; r3[7] -= m3 * s; }
// choose pivot - or die
if (fabs(r3[1])>fabs(r2[1])) SWAP_ROWS(r3, r2);
if (fabs(r2[1])>fabs(r1[1])) SWAP_ROWS(r2, r1);
// if (0.0 == r1[1]) return GL_FALSE;
// eliminate second variable
m2 = r2[1]/r1[1]; m3 = r3[1]/r1[1];
r2[2] -= m2 * r1[2]; r3[2] -= m3 * r1[2];
r2[3] -= m2 * r1[3]; r3[3] -= m3 * r1[3];
s = r1[4]; if (0.0 != s) { r2[4] -= m2 * s; r3[4] -= m3 * s; }
s = r1[5]; if (0.0 != s) { r2[5] -= m2 * s; r3[5] -= m3 * s; }
s = r1[6]; if (0.0 != s) { r2[6] -= m2 * s; r3[6] -= m3 * s; }
s = r1[7]; if (0.0 != s) { r2[7] -= m2 * s; r3[7] -= m3 * s; }
// choose pivot - or die
if (fabs(r3[2])>fabs(r2[2])) SWAP_ROWS(r3, r2);
// if (0.0 == r2[2]) return GL_FALSE;
// eliminate third variable
m3 = r3[2]/r2[2];
r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4],
r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6],
r3[7] -= m3 * r2[7];
// last check
// if (0.0 == r3[3]) return GL_FALSE;
s = 1.0f/r3[3]; // now back substitute row 3
r3[4] *= s; r3[5] *= s; r3[6] *= s; r3[7] *= s;
m2 = r2[3]; // now back substitute row 2
s = 1.0f/r2[2];
r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2),
r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2);
m1 = r1[3];
r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1,
r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1;
m0 = r0[3];
r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0,
r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0;
m1 = r1[2]; // now back substitute row 1
s = 1.0f/r1[1];
r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1),
r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1);
m0 = r0[2];
r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0,
r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0;
m0 = r0[1]; // now back substitute row 0
s = 1.0f/r0[0];
r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0),
r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0);
Vector4 Row0(r0[4], r1[4], r2[4], r3[4]);
Vector4 Row1(r0[5], r1[5], r2[5], r3[5]);
Vector4 Row2(r0[6], r1[6], r2[6], r3[6]);
Vector4 Row3(r0[7], r1[7], r2[7], r3[7]);
Matrix44 out(Row0, Row1, Row2, Row3);
return out;
#undef MAT
#undef SWAP_ROWS
}
// ============================================================================
// Project/Unproject a point.
// Convert world coordinates to screen coordinates.
Vector3 ProjectPoint(const Vector3& Pt, const Matrix44& ModelView, const Matrix44& Projection, const int Viewport[4])
{
Vector4 Tmp;
Tmp = Mul4(Vector4(Pt[0], Pt[1], Pt[2], 1.0f), ModelView);
Tmp = Mul4(Tmp, Projection);
// Normalize.
Tmp /= Tmp[3];
// Screen coordinates.
return Vector3(Viewport[0]+(1+Tmp[0])*Viewport[2]/2, Viewport[1]+(1+Tmp[1])*Viewport[3]/2, (1+Tmp[2])/2);
}
void ProjectPoints(Vector3* Points, int NumPoints, const Matrix44& ModelView, const Matrix44& Projection, const int Viewport[4])
{
for (int i = 0; i < NumPoints; i++)
{
Vector4 Tmp;
Tmp = Mul4(Vector4(Points[i][0], Points[i][1], Points[i][2], 1.0f), ModelView);
Tmp = Mul4(Tmp, Projection);
// Normalize.
Tmp /= Tmp[3];
// Screen coordinates.
Points[i] = Vector3(Viewport[0]+(1+Tmp[0])*Viewport[2]/2, Viewport[1]+(1+Tmp[1])*Viewport[3]/2, (1+Tmp[2])/2);
}
}
// Convert screen coordinates to world coordinates.
Vector3 UnprojectPoint(const Vector3& Point, const Matrix44& ModelView, const Matrix44& Projection, const int Viewport[4])
{
Vector3 Tmp = Point;
UnprojectPoints(&Tmp, 1, ModelView, Projection, Viewport);
return Tmp;
}
void UnprojectPoints(Vector3* Points, int NumPoints, const Matrix44& ModelView, const Matrix44& Projection, const int Viewport[4])
{
// Calculate the screen to model transform.
Matrix44 Transform = Inverse(Mul(ModelView, Projection));
for (int i = 0; i < NumPoints; i++)
{
Vector4 Tmp;
// Convert the point to homogeneous coordinates.
Tmp[0] = (Points[i][0] - Viewport[0]) * 2.0f / Viewport[2] - 1.0f;
Tmp[1] = (Points[i][1] - Viewport[1]) * 2.0f / Viewport[3] - 1.0f;
Tmp[2] = Points[i][2] * 2.0f - 1.0f;
Tmp[3] = 1.0f;
Tmp = Mul4(Tmp, Transform);
if (Tmp[3] != 0.0f)
Tmp /= Tmp[3];
Points[i] = Vector3(Tmp[0], Tmp[1], Tmp[2]);
}
}
// ============================================================================
// Geometry functions.
// Sutherland-Hodgman method of clipping a polygon to a plane.
void PolygonPlaneClip(Vector3* InPoints, int NumInPoints, Vector3* OutPoints, int* NumOutPoints, const Vector4& Plane)
{
Vector3 *s, *p, i;
*NumOutPoints = 0;
s = &InPoints[NumInPoints-1];
for (int j = 0; j < NumInPoints; j++)
{
p = &InPoints[j];
if (Dot3(*p, Plane) + Plane[3] <= 0)
{
if (Dot3(*s, Plane) + Plane[3] <= 0)
{
// Both points inside.
OutPoints[*NumOutPoints] = *p;
*NumOutPoints = *NumOutPoints + 1;
}
else
{
// Outside, inside.
LinePlaneIntersection(i, *s, *p, Plane);
OutPoints[*NumOutPoints] = i;
*NumOutPoints = *NumOutPoints + 1;
OutPoints[*NumOutPoints] = *p;
*NumOutPoints = *NumOutPoints + 1;
}
}
else
{
if (Dot3(*s, Plane) + Plane[3] <= 0)
{
// Inside, outside.
LinePlaneIntersection(i, *s, *p, Plane);
OutPoints[*NumOutPoints] = i;
*NumOutPoints = *NumOutPoints + 1;
}
}
s = p;
}
}
// Calculate the intersection of a line segment and a plane and returns false
// if they are parallel or the intersection is outside the line segment.
bool LinePlaneIntersection(Vector3& Intersection, const Vector3& Start, const Vector3& End, const Vector4& Plane)
{
Vector3 Dir = End - Start;
float t1 = Dot3(Plane, Start) + Plane[3];
float t2 = Dot3(Plane, Dir);
if (t2 == 0.0f)
return false;
float t = -t1 / t2;
Intersection = Start + t * Dir;
if ((t < 0.0f) || (t > 1.0f))
return false;
return true;
}
bool LineTriangleMinIntersection(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& Start, const Vector3& End, float& MinDist, Vector3& Intersection)
{
// Calculate the polygon plane.
Vector4 Plane;
Plane = Cross3(p1 - p2, p3 - p2);
Plane[3] = -Dot3(Plane, p1);
// Check if the line is parallel to the plane.
Vector3 Dir = End - Start;
float t1 = Dot3(Plane, Start) + Plane[3];
float t2 = Dot3(Plane, Dir);
if (t2 == 0)
return false;
float t = -(t1 / t2);
if (t < 0)
return false;
// Intersection of the plane and line segment.
Intersection = Start - (t1 / t2) * Dir;
float Dist = (Start - Intersection).Length();
if (Dist > MinDist)
return false;
// Check if we're inside the triangle.
Vector3 pa1, pa2, pa3;
pa1 = (p1 - Intersection).Normalize();
pa2 = (p2 - Intersection).Normalize();
pa3 = (p3 - Intersection).Normalize();
float a1, a2, a3;
a1 = Dot3(pa1, pa2);
a2 = Dot3(pa2, pa3);
a3 = Dot3(pa3, pa1);
float total = (acosf(a1) + acosf(a2) + acosf(a3)) * RTOD;
if (fabs(total - 360) <= 0.001f)
{
MinDist = Dist;
return true;
}
return false;
}
bool LineQuadMinIntersection(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4, const Vector3& Start, const Vector3& End, float& MinDist, Vector3& Intersection)
{
// Calculate the polygon plane.
Vector4 Plane;
Plane = Cross3(p1 - p2, p3 - p2);
Plane[3] = -Dot3(Plane, p1);
// Check if the line is parallel to the plane.
Vector3 Dir = End - Start;
float t1 = Dot3(Plane, Start) + Plane[3];
float t2 = Dot3(Plane, Dir);
if (t2 == 0)
return false;
float t = -(t1 / t2);
if (t < 0)
return false;
// Intersection of the plane and line segment.
Intersection = Start - (t1 / t2) * Dir;
float Dist = (Start - Intersection).Length();
if (Dist > MinDist)
return false;
// Check if we're inside the triangle.
Vector3 pa1, pa2, pa3;
pa1 = (p1 - Intersection).Normalize();
pa2 = (p2 - Intersection).Normalize();
pa3 = (p3 - Intersection).Normalize();
float a1, a2, a3;
a1 = Dot3(pa1, pa2);
a2 = Dot3(pa2, pa3);
a3 = Dot3(pa3, pa1);
float total = (acosf(a1) + acosf(a2) + acosf(a3)) * RTOD;
if (fabs(total - 360) <= 0.001f)
{
MinDist = Dist;
return true;
}
// Check if we're inside the second triangle.
pa2 = (p4 - Intersection).Normalize();
a1 = Dot3(pa1, pa2);
a2 = Dot3(pa2, pa3);
a3 = Dot3(pa3, pa1);
total = (acosf(a1) + acosf(a2) + acosf(a3)) * RTOD;
if (fabs(total - 360) <= 0.001f)
{
MinDist = Dist;
return true;
}
return false;
}

707
common/algebra.h Normal file
View file

@ -0,0 +1,707 @@
#ifndef _ALGEBRA_H_
#define _ALGEBRA_H_
#include <math.h>
//
// Simple math library and linear algebra functions.
//
// Everything is based on the Vector4 class, so changing that class should be enough
// to add support for compiler specific math intrinsics.
//
// Functions that end with 34 mean that they don't care what happens to the 4th
// component, it can either be affected or not.
//
// Matrices are represented as row-major, so we pre-multiply instead of post-multiplying
// like you would in a column major notation.
//
// OpenGL only expects a matrix to be an array of 16 floats so it doesn't matter what
// notation we use.
//
// v[0] v[1] v[2] v[3] <- x, y, z, w
//
// m[0] m[1] m[2] m[3] <- x axis
// m[4] m[5] m[6] m[7] <- y axis
// m[8] m[9] m[10] m[11] <- z axis
// m[12] m[13] m[14] m[15] <- translation
//
// TODO: Move this define to config.h
#define LC_MATH_FLOAT
//#define LC_MATH_SSE
// Classes defined in this file:
class Vector3;
class Vector4;
class Quaternion;
class Matrix44;
// ============================================================================
// Vector4 class (float version).
#ifdef LC_MATH_FLOAT
class Vector4
{
public:
// Constructors.
inline Vector4() { }
inline explicit Vector4(const float _x, const float _y, const float _z)
: x(_x), y(_y), z(_z) { }
inline explicit Vector4(const float _x, const float _y, const float _z, const float _w)
: x(_x), y(_y), z(_z), w(_w) { }
inline operator const float*() const { return (const float*)this; }
inline float& operator[](int i) const { return ((float*)this)[i]; }
// Comparison.
friend inline bool operator==(const Vector4& a, const Vector4& b)
{ return (a.x == b.x) && (a.y == b.y) && (a.z == b.z) && (a.w == b.w); }
friend inline bool Compare3(const Vector4& a, const Vector4& b)
{ return (a.x == b.x) && (a.y == b.y) && (a.z == b.z); }
// Math operations for 4 components.
friend inline Vector4 operator+(const Vector4& a, const Vector4& b)
{ return Vector4(a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w); }
friend inline Vector4 operator-(const Vector4& a, const Vector4& b)
{ return Vector4(a.x-b.x, a.y-b.y, a.z-b.z, a.w-b.w); }
friend inline Vector4 operator*(const Vector4& a, float f)
{ return Vector4(a.x*f, a.y*f, a.z*f, a.w*f); }
friend inline Vector4 operator*(const Vector4& a, const Vector4& b)
{ return Vector4(a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w); }
friend inline Vector4 operator/(const Vector4& a, float f)
{ return Vector4(a.x/f, a.y/f, a.z/f, a.w/f); }
friend inline Vector4 operator/=(Vector4& a, float f)
{ a = Vector4(a.x/f, a.y/f, a.z/f, a.w/f); return a; }
friend inline Vector4 operator-(const Vector4& a)
{ return Vector4(-a.x, -a.y, -a.z, -a.w); }
// Math operations ignoring the 4th component.
friend inline Vector4 Add34(const Vector4& a, const Vector4& b)
{ return Vector4(a.x+b.x, a.y+b.y, a.z+b.z); }
friend inline Vector4 Subtract34(const Vector4& a, const Vector4& b)
{ return Vector4(a.x-b.x, a.y-b.y, a.z-b.z); }
friend inline Vector4 Multiply34(const Vector4& a, float f)
{ return Vector4(a.x*f, a.y*f, a.z*f); }
friend inline Vector4 Multiply34(const Vector4& a, const Vector4& b)
{ return Vector4(a.x*b.x, a.y*b.y, a.z*b.z); }
friend inline Vector4 Divide34(const Vector4& a, float f)
{ return Vector4(a.x/f, a.y/f, a.z/f); }
friend inline Vector4 Negate34(const Vector4& a)
{ return Vector4(-a.x, -a.y, -a.z, -a.w); }
// Dot product.
friend inline float Dot3(const Vector4& a, const Vector4& b)
{ return a.x*b.x + a.y*b.y + a.z*b.z; }
friend inline float Dot4(const Vector4& a, const Vector4& b)
{ return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; }
// Cross product.
friend inline Vector4 Cross3(const Vector4& a, const Vector4& b)
{ return Vector4(a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x); }
// Other functions.
inline float Length3() const
{ return sqrtf(x*x + y*y + z*z); }
inline void Normalize34()
{
float len = 1.0f / sqrtf(x*x + y*y + z*z);
x *= len;
y *= len;
z *= len;
}
inline void Abs34()
{
if (x < 0.0f) x = -x;
if (y < 0.0f) y = -y;
if (z < 0.0f) z = -z;
}
inline void Abs()
{
if (x < 0.0f) x = -x;
if (y < 0.0f) y = -y;
if (z < 0.0f) z = -z;
if (w < 0.0f) w = -w;
}
protected:
float x, y, z, w;
};
#endif
// ============================================================================
// Vector4 class (SSE version).
#ifdef LC_MATH_SSE
// If you can't find this file you need to install the VS6 Processor Pack.
#include <xmmintrin.h>
class __declspec(align(16)) Vector4
{
public:
// Constructors.
inline Vector4() { }
inline explicit Vector4(const __m128& _xyzw)
: xyzw(_xyzw) { }
inline explicit Vector4(const float _x, const float _y, const float _z)
: xyzw(_mm_setr_ps(_x, _y, _z, _z)) { }
inline explicit Vector4(const float _x, const float _y, const float _z, const float _w)
: xyzw(_mm_setr_ps(_x, _y, _z, _w)) { }
inline float& operator[](int i) const { return ((const float*)this)[i]; }
// Comparison.
friend inline bool operator==(const Vector4& a, const Vector4& b)
{ return !_mm_movemask_ps(_mm_cmpneq_ps(a.xyzw, b.xyzw)); }
friend inline bool Compare3(const Vector4& a, const Vector4& b)
{ return (_mm_movemask_ps(_mm_cmpeq_ps(a.xyzw, b.xyzw)) & 0x7) == 0x7; }
// Math operations for 4 components.
friend inline Vector4 operator+(const Vector4& a, const Vector4& b)
{ return Vector4(_mm_add_ps(a.xyzw, b.xyzw)); }
friend inline Vector4 operator-(const Vector4& a, const Vector4& b)
{ return Vector4(_mm_sub_ps(a.xyzw, b.xyzw)); }
friend inline Vector4 operator*(const Vector4& a, float f)
{ return Vector4(_mm_mul_ps(a.xyzw, _mm_load_ps1(&f))); }
friend inline Vector4 operator*(const Vector4& a, const Vector4& b)
{ return Vector4(_mm_mul_ps(a.xyzw, b.xyzw)); }
friend inline Vector4 operator/(const Vector4& a, float f)
{ return Vector4(_mm_div_ps(a.xyzw, _mm_load_ps1(&f))); }
friend inline Vector4 operator-(const Vector4& a)
{
static const __declspec(align(16)) unsigned int Mask[4] = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }
return Vector4(_mm_xor_ps(xyzw, *(__m128*)&Mask));
}
// Math operations ignoring the 4th component.
friend inline Vector4 Add34(const Vector4& a, const Vector4& b)
{ return a*b }
friend inline Vector4 Subtract34(const Vector4& a, const Vector4& b)
{ return a-b; }
friend inline Vector4 Multiply34(const Vector4& a, float f)
{ return a*f; }
friend inline Vector4 Multiply34(const Vector4& a, const Vector4& b)
{ return a*b; }
friend inline Vector4 Divide34(const Vector4& a, float f)
{ return a/f; }
friend inline Vector4 Negate34(const Vector4& a)
{ return -a; }
// Dot product.
friend inline float Dot3(const Vector4& a, const Vector4& b)
{
__m128 tmp = _mm_mul_ps(a.xyzw, b.xyzw);
__m128 yz = _mm_add_ss(_mm_shuffle_ps(tmp, tmp, _MM_SHUFFLE(1, 1, 1, 1)), _mm_shuffle_ps(tmp, tmp, _MM_SHUFFLE(2, 2, 2, 2)));
tmp = _mm_add_ss(tmp, yz);
return *(const float*)&tmp;
}
// Cross product.
friend inline Vector4 Cross3(const Vector4& a, const Vector4& b)
{
// a(yzx)*b(zxy)-a(zxy)*b(yzx)
__m128 r1 = _mm_mul_ps(_mm_shuffle_ps(a.xyzw, a.xyzw, _MM_SHUFFLE(0, 0, 2, 1)), _mm_shuffle_ps(b.xyzw, b.xyzw, _MM_SHUFFLE(0, 1, 0, 2)));
__m128 r2 = _mm_mul_ps(_mm_shuffle_ps(a.xyzw, a.xyzw, _MM_SHUFFLE(0, 1, 0, 2)), _mm_shuffle_ps(b.xyzw, b.xyzw, _MM_SHUFFLE(0, 0, 2, 1)));
return Vector4(_mm_sub_ps(r1, r2));
}
// Other functions.
inline float Length3() const
{
__m128 tmp = _mm_mul_ps(xyzw, xyzw);
__m128 yz = _mm_add_ss(_mm_shuffle_ps(tmp, tmp, _MM_SHUFFLE(1, 1, 1, 1)), _mm_shuffle_ps(tmp, tmp, _MM_SHUFFLE(2, 2, 2, 2)));
tmp = _mm_add_ss(tmp, yz);
tmp = _mm_sqrt_ss(tmp);
return *(const float*)&tmp;
}
inline void Normalize34()
{
__m128 tmp = _mm_mul_ps(xyzw, xyzw);
__m128 yz = _mm_add_ss(_mm_shuffle_ps(tmp, tmp, _MM_SHUFFLE(1, 1, 1, 1)), _mm_shuffle_ps(tmp, tmp, _MM_SHUFFLE(2, 2, 2, 2)));
tmp = _mm_add_ss(tmp, yz);
tmp = _mm_rsqrt_ss(tmp);
tmp = _mm_shuffle_ps(tmp, tmp, _MM_SHUFFLE(0, 0, 0, 0));
xyzw = _mm_mul_ps(xyzw, tmp);
}
inline void Abs()
{
static const __declspec(align(16)) unsigned int Mask[4] = { 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff }
xyzw = _mm_and_ps(xyzw, *(__m128*)&Mask);
}
protected:
__m128 xyzw;
};
#endif
// ============================================================================
// 3D Vector class.
class Vector3
{
public:
// Constructors.
inline Vector3()
{ }
inline explicit Vector3(const Vector4& _v)
: m_Value(_v) { }
inline explicit Vector3(const float _x, const float _y, const float _z)
: m_Value(_x, _y, _z) { }
inline operator const float*() const { return (const float*)this; }
inline operator float*() { return (float*)this; }
inline const Vector4& GetValue() const { return m_Value; }
inline operator const Vector4() const
{ return Vector4(m_Value[0], m_Value[1], m_Value[2], 0.0f); }
inline float& operator[](int i) const { return m_Value[i]; }
// Math operations.
friend inline Vector3 operator+=(Vector3& a, const Vector3& b)
{ a.m_Value = a.m_Value + b.m_Value; return a; }
friend inline Vector3 operator*=(Vector3& a, float b)
{ a.m_Value = a.m_Value * b; return a; }
friend inline Vector3 operator/=(Vector3& a, float b)
{ a.m_Value = a.m_Value / b; return a; }
// Other functions.
inline float Length() const
{ return m_Value.Length3(); }
inline float LengthSquared() const
{ return Dot3(m_Value, m_Value); }
inline const Vector3& Normalize()
{ m_Value.Normalize34(); return *this; }
inline void Abs()
{ m_Value.Abs34(); }
protected:
Vector4 m_Value;
};
// ============================================================================
// Operators.
// Comparison.
inline bool operator==(const Vector3& a, const Vector3& b)
{ return Compare3(a.GetValue(), b.GetValue()); }
// Multiply by a scalar.
inline Vector3 operator*(const Vector3& a, float f)
{ return Vector3(Multiply34(a.GetValue(), f)); }
inline Vector3 operator*(float f, const Vector3& a)
{ return Vector3(Multiply34(a.GetValue(), f)); }
// Divide by a scalar.
inline Vector3 operator/(const Vector3& a, float f)
{ return Vector3(Divide34(a.GetValue(), f)); }
inline Vector3 operator/(float f, const Vector3& a)
{ return Vector3(Divide34(a.GetValue(), f)); }
// Add vectors.
inline Vector3 operator+(const Vector3& a, const Vector3& b)
{ return Vector3(Add34(a.GetValue(), b.GetValue())); }
// Subtract vectors.
inline Vector3 operator-(const Vector3& a, const Vector3& b)
{ return Vector3(Subtract34(a.GetValue(), b.GetValue())); }
// Negate.
inline Vector3 operator-(const Vector3& a)
{ return Vector3(Negate34(a.GetValue())); }
// Dot product.
inline float Dot3(const Vector3& a, const Vector3& b)
{ return Dot3(a.GetValue(), b.GetValue()); }
// Cross product.
inline Vector3 Cross3(const Vector3& a, const Vector3& b)
{ return Vector3(Cross3(a.GetValue(), b.GetValue())); }
// ============================================================================
// Quaternion class.
class Quaternion
{
public:
// Constructors.
inline Quaternion()
{ }
inline explicit Quaternion(const Vector4& _v)
: m_Value(_v) { }
inline explicit Quaternion(const float _x, const float _y, const float _z, const float _w)
: m_Value(_x, _y, _z, _w) { }
// Get/Set functions.
inline const float operator[](int i) const { return m_Value[i]; }
// Conversions.
inline void FromAxisAngle(const Vector4& AxisAngle)
{
float s = sinf(AxisAngle[3] / 2.0f);
m_Value = Vector4(AxisAngle[0] * s, AxisAngle[1] * s, AxisAngle[2] * s, cosf(AxisAngle[3] / 2.0f));
}
inline void CreateRotationX(float Radians)
{
m_Value = Vector4(sinf(Radians / 2.0f), 0, 0, cosf(Radians / 2.0f));
}
inline void CreateRotationY(float Radians)
{
m_Value = Vector4(0, sinf(Radians / 2.0f), 0, cosf(Radians / 2.0f));
}
inline void CreateRotationZ(float Radians)
{
m_Value = Vector4(0, 0, sinf(Radians / 2.0f), cosf(Radians / 2.0f));
}
inline void ToAxisAngle(Vector4& AxisAngle) const
{
float Len = m_Value[0]*m_Value[0] + m_Value[1]*m_Value[1] + m_Value[2]*m_Value[2];
if (Len > 0.0001f)
{
float f = 1.0f / sqrtf(Len);
AxisAngle = Vector4(m_Value[0] * f, m_Value[1] * f, m_Value[2] * f, acosf(m_Value[3]) * 2.0f);
}
else
{
AxisAngle = Vector4(0, 0, 1, 0);
}
}
// Operators.
friend inline Quaternion Mul(const Quaternion& a, const Quaternion& b)
{
float x = a.m_Value[0] * b.m_Value[3] + a.m_Value[1] * b.m_Value[2] - a.m_Value[2] * b.m_Value[1] + a.m_Value[3] * b.m_Value[0];
float y = -a.m_Value[0] * b.m_Value[2] + a.m_Value[1] * b.m_Value[3] + a.m_Value[2] * b.m_Value[0] + a.m_Value[3] * b.m_Value[1];
float z = a.m_Value[0] * b.m_Value[1] - a.m_Value[1] * b.m_Value[0] + a.m_Value[2] * b.m_Value[3] + a.m_Value[3] * b.m_Value[2];
float w = -a.m_Value[0] * b.m_Value[0] - a.m_Value[1] * b.m_Value[1] - a.m_Value[2] * b.m_Value[2] + a.m_Value[3] * b.m_Value[3];
return Quaternion(x, y, z, w);
}
friend inline Vector3 Mul(const Vector3& a, const Quaternion& b)
{
// Faster to transform to a matrix and multiply.
float Tx = 2.0f*b[0];
float Ty = 2.0f*b[1];
float Tz = 2.0f*b[2];
float Twx = Tx*b[3];
float Twy = Ty*b[3];
float Twz = Tz*b[3];
float Txx = Tx*b[0];
float Txy = Ty*b[0];
float Txz = Tz*b[0];
float Tyy = Ty*b[1];
float Tyz = Tz*b[1];
float Tzz = Tz*b[2];
Vector3 Rows[3];
Rows[0] = Vector3(1.0f-(Tyy+Tzz), Txy+Twz, Txz-Twy);
Rows[1] = Vector3(Txy-Twz, 1.0f-(Txx+Tzz), Tyz+Twx);
Rows[2] = Vector3(Txz+Twy, Tyz-Twx, 1.0f-(Txx+Tyy));
return Vector3(Rows[0].GetValue()*a[0] + Rows[1].GetValue()*a[1] + Rows[2].GetValue()*a[2]);
}
protected:
Vector4 m_Value;
};
// ============================================================================
// 3x3 Matrix class.
class Matrix33
{
public:
// Constructors.
inline Matrix33()
{ }
inline Matrix33(const Vector3& Row0, const Vector3& Row1, const Vector3& Row2)
{ m_Rows[0] = Row0; m_Rows[1] = Row1; m_Rows[2] = Row2; }
inline void LoadIdentity()
{
m_Rows[0] = Vector3(1.0f, 0.0f, 0.0f);
m_Rows[1] = Vector3(0.0f, 1.0f, 0.0f);
m_Rows[2] = Vector3(0.0f, 0.0f, 1.0f);
}
inline void CreateFromAxisAngle(const Vector3& Axis, const float Radians)
{
float s, c, mag, xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
s = sinf(Radians);
c = cosf(Radians);
mag = Axis.Length();
if (mag == 0.0f)
{
LoadIdentity();
return;
}
Vector3 Normal = Axis * (1.0f / mag);
xx = Normal[0] * Normal[0];
yy = Normal[1] * Normal[1];
zz = Normal[2] * Normal[2];
xy = Normal[0] * Normal[1];
yz = Normal[1] * Normal[2];
zx = Normal[2] * Normal[0];
xs = Normal[0] * s;
ys = Normal[1] * s;
zs = Normal[2] * s;
one_c = 1.0f - c;
m_Rows[0] = Vector3((one_c * xx) + c, (one_c * xy) + zs, (one_c * zx) - ys);
m_Rows[1] = Vector3((one_c * xy) - zs, (one_c * yy) + c, (one_c * yz) + xs);
m_Rows[2] = Vector3((one_c * zx) + ys, (one_c * yz) - xs, (one_c * zz) + c);
}
friend inline Vector3 Mul(const Vector3& a, const Matrix33& b)
{ return Vector3(b.m_Rows[0]*a[0] + b.m_Rows[1]*a[1] + b.m_Rows[2]*a[2]); }
protected:
Vector3 m_Rows[3];
friend class Matrix44;
};
// ============================================================================
// 4x4 Matrix class.
class Matrix44
{
public:
inline Matrix44()
{ }
inline Matrix44(const Vector4& Row0, const Vector4& Row1, const Vector4& Row2, const Vector4& Row3)
{ m_Rows[0] = Row0; m_Rows[1] = Row1; m_Rows[2] = Row2; m_Rows[3] = Row3; }
inline operator const float*() const { return (const float*)this; }
inline Vector4& operator[](int i) { return m_Rows[i]; }
inline void LoadIdentity()
{
m_Rows[0] = Vector4(1.0f, 0.0f, 0.0f, 0.0f);
m_Rows[1] = Vector4(0.0f, 1.0f, 0.0f, 0.0f);
m_Rows[2] = Vector4(0.0f, 0.0f, 1.0f, 0.0f);
m_Rows[3] = Vector4(0.0f, 0.0f, 0.0f, 1.0f);
}
// Math operations.
friend inline Vector3 Mul31(const Vector3& a, const Matrix44& b)
{ return Vector3(b.m_Rows[0]*a[0] + b.m_Rows[1]*a[1] + b.m_Rows[2]*a[2] + b.m_Rows[3]); }
friend inline Vector3 Mul30(const Vector3& a, const Matrix44& b)
{ return Vector3(b.m_Rows[0]*a[0] + b.m_Rows[1]*a[1] + b.m_Rows[2]*a[2]); }
friend inline Vector4 Mul4(const Vector4& a, const Matrix44& b)
{ return Vector4(b.m_Rows[0]*a[0] + b.m_Rows[1]*a[1] + b.m_Rows[2]*a[2] + b.m_Rows[3]*a[3]); }
friend inline Matrix44 Mul(const Matrix44& a, const Matrix44& b)
{
Vector4 Col0(b.m_Rows[0][0], b.m_Rows[1][0], b.m_Rows[2][0], b.m_Rows[3][0]);
Vector4 Col1(b.m_Rows[0][1], b.m_Rows[1][1], b.m_Rows[2][1], b.m_Rows[3][1]);
Vector4 Col2(b.m_Rows[0][2], b.m_Rows[1][2], b.m_Rows[2][2], b.m_Rows[3][2]);
Vector4 Col3(b.m_Rows[0][3], b.m_Rows[1][3], b.m_Rows[2][3], b.m_Rows[3][3]);
Vector4 Ret0(Dot4(a.m_Rows[0], Col0), Dot4(a.m_Rows[0], Col1), Dot4(a.m_Rows[0], Col2), Dot4(a.m_Rows[0], Col3));
Vector4 Ret1(Dot4(a.m_Rows[1], Col0), Dot4(a.m_Rows[1], Col1), Dot4(a.m_Rows[1], Col2), Dot4(a.m_Rows[1], Col3));
Vector4 Ret2(Dot4(a.m_Rows[2], Col0), Dot4(a.m_Rows[2], Col1), Dot4(a.m_Rows[2], Col2), Dot4(a.m_Rows[2], Col3));
Vector4 Ret3(Dot4(a.m_Rows[3], Col0), Dot4(a.m_Rows[3], Col1), Dot4(a.m_Rows[3], Col2), Dot4(a.m_Rows[3], Col3));
return Matrix44(Ret0, Ret1, Ret2, Ret3);
}
inline Matrix44& operator=(const Matrix33& a)
{
m_Rows[0] = Vector4(a.m_Rows[0][0], a.m_Rows[0][1], a.m_Rows[0][2], 0.0f);
m_Rows[1] = Vector4(a.m_Rows[1][0], a.m_Rows[1][1], a.m_Rows[1][2], 0.0f);
m_Rows[2] = Vector4(a.m_Rows[2][0], a.m_Rows[2][1], a.m_Rows[2][2], 0.0f);
m_Rows[3] = Vector4(0.0f, 0.0f, 0.0f, 1.0f);
return *this;
}
inline void Transpose3()
{
Vector4 a = m_Rows[0], b = m_Rows[1], c = m_Rows[2];
m_Rows[0] = Vector4(a[0], b[0], c[0], a[3]);
m_Rows[1] = Vector4(a[1], b[1], c[1], b[3]);
m_Rows[2] = Vector4(a[2], b[2], c[2], c[3]);
}
inline void SetTranslation(const Vector3& a)
{ m_Rows[3] = Vector4(a[0], a[1], a[2], 1.0f); }
friend Matrix44 Inverse(const Matrix44& m);
void CreateLookAt(const Vector3& Eye, const Vector3& Target, const Vector3& Up);
void CreatePerspective(float FoVy, float Aspect, float Near, float Far);
void CreateFromAxisAngle(const Vector3& Axis, float Radians)
{
Matrix33 Mat;
Mat.CreateFromAxisAngle(Axis, Radians);
*this = Mat;
}
Vector4 ToAxisAngle()
{
Matrix33 tmp(Vector3(m_Rows[0]).Normalize(), Vector3(m_Rows[1]).Normalize(), Vector3(m_Rows[2]).Normalize());
// Determinant should be 1 for rotation matrices.
float Determinant = tmp.m_Rows[0][0] * tmp.m_Rows[1][1] * tmp.m_Rows[2][2] + tmp.m_Rows[0][1] * tmp.m_Rows[1][2] * tmp.m_Rows[2][0] +
tmp.m_Rows[0][2] * tmp.m_Rows[1][0] * tmp.m_Rows[2][1] - tmp.m_Rows[0][0] * tmp.m_Rows[1][2] * tmp.m_Rows[2][1] -
tmp.m_Rows[0][1] * tmp.m_Rows[1][0] * tmp.m_Rows[2][2] - tmp.m_Rows[0][2] * tmp.m_Rows[1][1] * tmp.m_Rows[2][0];
if (Determinant < 0.0f)
tmp.m_Rows[0] *= -1.0f;
float Trace = tmp.m_Rows[0][0] + tmp.m_Rows[1][1] + tmp.m_Rows[2][2];
float Cos = 0.5f * (Trace - 1.0f);
Vector4 rot;
if (Cos < -1.0f)
Cos = -1.0f;
else if (Cos > 1.0f)
Cos = 1.0f;
rot[3] = acosf(Cos); // in [0,PI]
if (rot[3] > 0.01f)
{
if (fabsf(3.141592f - rot[3]) > 0.01f)
{
rot[0] = tmp.m_Rows[1][2] - tmp.m_Rows[2][1];
rot[1] = tmp.m_Rows[2][0] - tmp.m_Rows[0][2];
rot[2] = tmp.m_Rows[0][1] - tmp.m_Rows[1][0];
float inv = 1.0f / sqrtf(rot[0]*rot[0] + rot[1]*rot[1] + rot[2]*rot[2]);
rot[0] *= inv;
rot[1] *= inv;
rot[2] *= inv;
}
else
{
// angle is PI
float HalfInverse;
if (tmp.m_Rows[0][0] >= tmp.m_Rows[1][1])
{
// r00 >= r11
if (tmp.m_Rows[0][0] >= tmp.m_Rows[2][2])
{
// r00 is maximum diagonal term
rot[0] = 0.5f * sqrtf(tmp.m_Rows[0][0] - tmp.m_Rows[1][1] - tmp.m_Rows[2][2] + 1.0f);
HalfInverse = 0.5f / rot[0];
rot[1] = HalfInverse * tmp.m_Rows[1][0];
rot[2] = HalfInverse * tmp.m_Rows[2][0];
}
else
{
// r22 is maximum diagonal term
rot[2] = 0.5f * sqrtf(tmp.m_Rows[2][2] - tmp.m_Rows[0][0] - tmp.m_Rows[1][1] + 1.0f);
HalfInverse = 0.5f / rot[2];
rot[0] = HalfInverse * tmp.m_Rows[2][0];
rot[1] = HalfInverse * tmp.m_Rows[2][1];
}
}
else
{
// r11 > r00
if (tmp.m_Rows[1][1] >= tmp.m_Rows[2][2])
{
// r11 is maximum diagonal term
rot[1] = 0.5f * sqrtf(tmp.m_Rows[1][1] - tmp.m_Rows[0][0] - tmp.m_Rows[2][2] + 1.0f);
HalfInverse = 0.5f / rot[1];
rot[0] = HalfInverse * tmp.m_Rows[1][0];
rot[2] = HalfInverse * tmp.m_Rows[2][1];
}
else
{
// r22 is maximum diagonal term
rot[2] = 0.5f * sqrtf(tmp.m_Rows[2][2] - tmp.m_Rows[0][0] - tmp.m_Rows[1][1] + 1.0f);
HalfInverse = 0.5f / rot[2];
rot[0] = HalfInverse * tmp.m_Rows[2][0];
rot[1] = HalfInverse * tmp.m_Rows[2][1];
}
}
}
}
else
{
// The angle is 0 and the matrix is the identity.
rot[0] = 0.0f;
rot[1] = 0.0f;
rot[2] = 1.0f;
}
return rot;
}
protected:
Vector4 m_Rows[4];
};
// ============================================================================
// Other Functions.
Vector3 ProjectPoint(const Vector3& Point, const Matrix44& ModelView, const Matrix44& Projection, const int Viewport[4]);
void ProjectPoints(Vector3* Points, int NumPoints, const Matrix44& ModelView, const Matrix44& Projection, const int Viewport[4]);
Vector3 UnprojectPoint(const Vector3& Point, const Matrix44& ModelView, const Matrix44& Projection, const int Viewport[4]);
void UnprojectPoints(Vector3* Points, int NumPoints, const Matrix44& ModelView, const Matrix44& Projection, const int Viewport[4]);
void PolygonPlaneClip(Vector3* InPoints, int NumInPoints, Vector3* OutPoints, int* NumOutPoints, const Vector4& Plane);
bool LinePlaneIntersection(Vector3& Intersection, const Vector3& Start, const Vector3& End, const Vector4& Plane);
bool LineTriangleMinIntersection(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& Start, const Vector3& End, float& MinDist, Vector3& Intersection);
bool LineQuadMinIntersection(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4, const Vector3& Start, const Vector3& End, float& MinDist, Vector3& Intersection);
#endif

341
common/array.cpp Executable file
View file

@ -0,0 +1,341 @@
//
// Simple array classes
//
#include <stdlib.h>
#include <string.h>
template <class T>
PtrArray<T>::PtrArray(int nSize)
{
m_pData = NULL;
m_nLength = 0;
m_nAlloc = 0;
if(nSize != 0)
Expand(nSize);
}
template <class T>
PtrArray<T>::~PtrArray()
{
free(m_pData);
}
template <class T>
void PtrArray<T>::Expand(int nGrow)
{
if((m_nLength + nGrow) > m_nAlloc)
{
m_pData =(T**)realloc(m_pData,(m_nLength + nGrow) * sizeof(T*));
memset(m_pData + m_nLength, 0, nGrow * sizeof(T*));
m_nAlloc = m_nLength + nGrow;
}
}
template <class T>
void PtrArray<T>::RemoveAll()
{
m_nLength = 0;
}
template <class T>
T* PtrArray<T>::RemoveIndex(int nIndex)
{
T* ret = NULL;
if(nIndex < m_nLength)
{
ret = m_pData[nIndex];
if(nIndex != m_nLength - 1)
memmove(m_pData + nIndex, m_pData + nIndex + 1, sizeof(T*) *(m_nLength - nIndex - 1));
m_nLength--;
m_pData[m_nLength] = NULL;
}
return ret;
}
template <class T>
T* PtrArray<T>::RemovePointer(T* pObj)
{
int i;
for(i = 0; i < m_nLength; i++)
if(m_pData[i] == pObj)
return RemoveIndex(i);
return NULL;
}
template <class T>
void PtrArray<T>::Add(T* pObj)
{
Expand(1);
m_pData[m_nLength] = pObj;
m_nLength++;
}
template <class T>
void PtrArray<T>::AddSorted(T* pObj, LC_PTRARRAY_COMPARE_FUNC pFunc, void* pData)
{
int i;
for(i = 0; i < GetSize(); i++)
{
if(pFunc(pObj, m_pData[i], pData) < 0)
{
InsertAt(i, pObj);
return;
}
}
Add(pObj);
}
template <class T>
void PtrArray<T>::InsertAt(int nIndex, T* pObj)
{
if(nIndex >= m_nLength)
Expand(nIndex - m_nLength + 1);
else
Expand(1);
m_nLength++;
for(int i = m_nLength - 1; i > nIndex; i--)
m_pData[i] = m_pData[i-1];
m_pData[nIndex] = pObj;
}
template <class T>
int PtrArray<T>::FindIndex(T* Obj) const
{
for (int i = 0; i < m_nLength; i++)
if (m_pData[i] == Obj)
return i;
return -1;
}
template <class T>
void PtrArray<T>::Sort(LC_PTRARRAY_COMPARE_FUNC SortFunc, void* SortData)
{
int Count = GetSize();
if (Count <= 1)
return;
int i = 1;
bool Flipped;
do
{
Flipped = false;
for (int j = Count - 1; j >= i; --j)
{
T* a = m_pData[j];
T* b = m_pData[j-1];
if (SortFunc(b, a, SortData) > 0)
{
m_pData[j - 1] = a;
m_pData[j] = b;
Flipped = true;
}
}
} while ((++i < Count) && Flipped);
}
template <class T>
PtrArray<T>& PtrArray<T>::operator=(const PtrArray<T>& Array)
{
m_nLength = Array.m_nLength;
m_nAlloc = Array.m_nAlloc;
m_pData =(T**)realloc(m_pData, (m_nAlloc) * sizeof(T*));
memcpy(m_pData, Array.m_pData, (m_nAlloc) * sizeof(T*));
}
template <class T>
PtrArray<T>& PtrArray<T>::operator+=(const PtrArray<T>& Array)
{
Expand(Array.m_nLength);
memcpy(m_pData + m_nLength, Array.m_pData, Array.m_nLength * sizeof(T*));
m_nLength += Array.m_nLength;
return *this;
}
// ============================================================================
template <class T>
ObjArray<T>::ObjArray(int Size, int Grow)
{
m_Data = NULL;
m_Length = 0;
m_Alloc = 0;
m_Grow = Grow;
if (Size != 0)
Expand(Size);
}
template <class T>
ObjArray<T>::~ObjArray ()
{
delete[] m_Data;
}
template <class T>
void ObjArray<T>::Expand(int Grow)
{
if ((m_Length + Grow) > m_Alloc)
{
int NewSize = ((m_Length + Grow) / m_Grow + 1) * m_Grow;
T* NewData = new T[NewSize];
for (int i = 0; i < m_Length; i++)
NewData[i] = m_Data[i];
delete[] m_Data;
m_Data = NewData;
m_Alloc = NewSize;
}
}
template <class T>
void ObjArray<T>::RemoveAll()
{
m_Length = 0;
}
template <class T>
void ObjArray<T>::RemoveIndex(int Index)
{
m_Length--;
for (int i = Index; i < m_Length; i++)
m_Data[i] = m_Data[i+1];
}
template <class T>
void ObjArray<T>::Add(const T& Obj)
{
Expand(1);
m_Data[m_Length++] = Obj;
}
template <class T>
void ObjArray<T>::AddSorted (const T& Obj, LC_OBJARRAY_COMPARE_FUNC Func, void* SortData)
{
int i;
for (i = 0; i < GetSize(); i++)
{
if (Func(Obj, m_Data[i], SortData) < 0)
{
InsertAt (i, Obj);
return;
}
}
Add(Obj);
}
template <class T>
void ObjArray<T>::InsertAt(int Index, const T& Obj)
{
if (Index >= m_Length)
Expand(Index - m_Length + 1);
else
Expand(1);
m_Length++;
for (int i = m_Length - 1; i > Index; i--)
m_Data[i] = m_Data[i-1];
m_Data[Index] = Obj;
}
/*
// ============================================================================
// ObjectArray class
ObjectArray::ObjectArray (unsigned long nSize)
{
m_pData = NULL;
m_nLength = 0;
m_nAlloc = 0;
if (nSize != 0)
Expand (nSize);
}
ObjectArray::~ObjectArray ()
{
free (m_pData);
}
void ObjectArray::Expand (unsigned long nGrow)
{
if ((m_nLength + nGrow) > m_nAlloc)
{
m_pData = (Object**)realloc (m_pData, (m_nLength + nGrow) * sizeof (Object*));
memset (m_pData + m_nLength, 0, nGrow * sizeof (Object*));
m_nAlloc = m_nLength + nGrow;
}
}
void ObjectArray::SetSize (unsigned long nSize)
{
if (nSize > m_nLength)
Expand (nSize - m_nLength);
m_nLength = nSize;
}
Object* ObjectArray::RemoveIndex (unsigned long nIndex)
{
Object* ret = NULL;
if (nIndex < m_nLength)
{
ret = m_pData[nIndex];
if (nIndex != m_nLength - 1)
memmove (m_pData + nIndex, m_pData + nIndex + 1,
sizeof (Object*) * (m_nLength - nIndex - 1));
m_nLength--;
m_pData[m_nLength] = NULL;
}
return ret;
}
Object* ObjectArray::RemovePointer (Object* pObj)
{
unsigned long i;
for (i = 0; i < m_nLength; i++)
if (m_pData[i] == pObj)
return RemoveIndex (i);
return NULL;
}
void ObjectArray::Add (Object* pObj)
{
Expand (1);
m_pData[m_nLength++] = pObj;
}
*/

70
common/array.h Executable file
View file

@ -0,0 +1,70 @@
#ifndef _ARRAY_H_
#define _ARRAY_H_
template <class T>
class PtrArray
{
public:
PtrArray(int nSize = 0);
~PtrArray();
typedef int (*LC_PTRARRAY_COMPARE_FUNC)(const T* a, const T* b, void* data);
int GetSize() const
{ return m_nLength; }
T* RemoveIndex(int nIndex);
T* RemovePointer(T* pObj);
void RemoveAll();
void Add(T* pObj);
void AddSorted(T* pObj, LC_PTRARRAY_COMPARE_FUNC pFunc, void* pData);
void InsertAt(int nIndex, T* pObj);
int FindIndex(T* Obj) const;
void Sort(LC_PTRARRAY_COMPARE_FUNC SortFunc, void* SortData);
PtrArray<T>& operator=(const PtrArray<T>& Array);
PtrArray<T>& operator+=(const PtrArray<T>& Array);
T* operator [](int nIndex) const
{ return m_pData[nIndex]; }
protected:
void Expand(int nGrow);
T** m_pData;
int m_nLength;
int m_nAlloc;
};
template <class T>
class ObjArray
{
public:
ObjArray(int Size = 0, int Grow = 16);
~ObjArray();
typedef int (*LC_OBJARRAY_COMPARE_FUNC)(const T& A, const T& B, void* SortData);
int GetSize() const
{ return m_Length; }
void RemoveIndex(int Index);
void RemoveAll();
void Add(const T& Obj);
void AddSorted(const T& Obj, LC_OBJARRAY_COMPARE_FUNC Func, void* SortData);
void InsertAt(int Index, const T& Obj);
T& operator [](int Index) const
{ return m_Data[Index]; }
protected:
void Expand(int Grow);
T* m_Data;
int m_Length;
int m_Alloc;
int m_Grow;
};
#include "array.cpp"
#endif // _ARRAY_H_

102
common/basewnd.h Normal file
View file

@ -0,0 +1,102 @@
#ifndef _BASEWND_H_
#define _BASEWND_H_
#include <string.h>
// FIXME: move this to another place
#ifdef WIN32
#include "stdafx.h"
typedef CWnd* BaseWndXID;
typedef struct
{
CWnd* wnd;
int index;
UINT command;
} BaseMenuItem;
#endif
#ifdef LC_LINUX
#include <gtk/gtk.h>
typedef GtkWidget* BaseWndXID;
typedef struct
{
GtkWidget* widget;
GtkAccelGroup* accel;
} BaseMenuItem;
#endif
#ifdef LC_MACOSX
typedef void* BaseWndXID;
typedef struct
{
void* Dummy;
} BaseMenuItem;
#endif
// =============================================================================
// Message Box constants
#define LC_OK 1
#define LC_CANCEL 2
#define LC_ABORT 3
#define LC_RETRY 4
#define LC_IGNORE 5
#define LC_YES 6
#define LC_NO 7
#define LC_MB_OK 0x000
#define LC_MB_OKCANCEL 0x001
//#define LC_MB_ABORTRETRYIGNORE 0x002
#define LC_MB_YESNOCANCEL 0x003
#define LC_MB_YESNO 0x004
//#define LC_MB_RETRYCANCEL 0x005
#define LC_MB_ICONERROR 0x010
#define LC_MB_ICONQUESTION 0x020
#define LC_MB_ICONWARNING 0x030
#define LC_MB_ICONINFORMATION 0x040
#define LC_MB_TYPEMASK 0x00F
#define LC_MB_ICONMASK 0x0F0
// =============================================================================
class BaseWnd
{
public:
BaseWnd (BaseWnd *parent, int menu_count);
virtual ~BaseWnd ();
int MessageBox (const char* text, const char* caption="LeoCAD", int flags=LC_MB_OK|LC_MB_ICONINFORMATION);
void BeginWait ();
void EndWait ();
void SetTitle (const char *title);
void ShowMenuItem (int id, bool show);
void EnableMenuItem (int id, bool enable);
void CheckMenuItem (int id, bool check);
void SetMenuItemText (int id, const char *text);
BaseWndXID GetXID () const
{ return m_pXID; }
void SetXID (BaseWndXID id)
{ m_pXID = id; }
#ifdef LC_LINUX
// FIXME: remove
operator GtkWidget* () const
{ return m_pXID; }
#endif
BaseMenuItem* GetMenuItem (int id) const
{ return &m_pMenuItems[id]; }
void SetMenuItem (int id, BaseMenuItem* item)
{ memcpy (&m_pMenuItems[id], item, sizeof (BaseMenuItem)); }
protected:
BaseWnd* m_pParent;
BaseWndXID m_pXID;
BaseMenuItem* m_pMenuItems;
};
#endif // _BASEWND_H_

852
common/camera.cpp Normal file
View file

@ -0,0 +1,852 @@
// Camera object.
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "opengl.h"
#include "globals.h"
#include "defines.h"
#include "vector.h"
#include "matrix.h"
#include "file.h"
#include "camera.h"
#include "tr.h"
#define LC_CAMERA_SAVE_VERSION 6 // LeoCAD 0.73
GLuint Camera::m_nTargetList = 0;
static LC_OBJECT_KEY_INFO camera_key_info[LC_CK_COUNT] =
{
{ "Camera Position", 3, LC_CK_EYE },
{ "Camera Target", 3, LC_CK_TARGET },
{ "Camera Up Vector", 3, LC_CK_UP }
};
// =============================================================================
// CameraTarget class
CameraTarget::CameraTarget (Camera *pParent)
: Object (LC_OBJECT_CAMERA_TARGET)
{
m_pParent = pParent;
/*
strcpy (m_strName, pParent->GetName ());
m_strName[LC_OBJECT_NAME_LEN-8] = '\0';
strcat (m_strName, ".Target");
*/
}
CameraTarget::~CameraTarget ()
{
}
void CameraTarget::MinIntersectDist (LC_CLICKLINE* pLine)
{
float dist = (float)BoundingBoxIntersectDist (pLine);
if (dist < pLine->mindist)
{
pLine->mindist = dist;
pLine->pClosest = this;
}
}
void CameraTarget::Select (bool bSelecting, bool bFocus, bool bMultiple)
{
m_pParent->SelectTarget (bSelecting, bFocus, bMultiple);
}
const char* CameraTarget::GetName() const
{
return m_pParent->GetName();
}
/////////////////////////////////////////////////////////////////////////////
// Camera construction/destruction
Camera::Camera ()
: Object (LC_OBJECT_CAMERA)
{
Initialize();
}
// Start with a standard camera.
Camera::Camera (unsigned char nType, Camera* pPrev)
: Object (LC_OBJECT_CAMERA)
{
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 } };
Initialize();
ChangeKey (1, false, true, eyes[nType], LC_CK_EYE);
ChangeKey (1, false, true, ups[nType], LC_CK_UP);
ChangeKey (1, true, true, eyes[nType], LC_CK_EYE);
ChangeKey (1, true, true, ups[nType], LC_CK_UP);
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 (const float *eye, const float *target, const float *up, Camera* pCamera)
: Object (LC_OBJECT_CAMERA)
{
// 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();
ChangeKey (1, false, true, eye, LC_CK_EYE);
ChangeKey (1, false, true, target, LC_CK_TARGET);
ChangeKey (1, false, true, upvec, LC_CK_UP);
ChangeKey (1, true, true, eye, LC_CK_EYE);
ChangeKey (1, true, true, target, LC_CK_TARGET);
ChangeKey (1, true, true, upvec, LC_CK_UP);
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)
: Object (LC_OBJECT_CAMERA)
{
// Fix the up vector
Vector upvec(0,0,1), frontvec(ex-tx, ey-ty, ez-tz), sidevec;
frontvec.Normalize();
if (frontvec == upvec)
sidevec = Vector(1,0,0);
else
sidevec.Cross(frontvec, upvec);
upvec.Cross(sidevec, frontvec);
upvec.Normalize();
Initialize();
float eye[3] = { ex, ey, ez }, target[3] = { tx, ty, tz };
ChangeKey (1, false, true, eye, LC_CK_EYE);
ChangeKey (1, false, true, target, LC_CK_TARGET);
ChangeKey (1, false, true, upvec, LC_CK_UP);
ChangeKey (1, true, true, eye, LC_CK_EYE);
ChangeKey (1, true, true, target, LC_CK_TARGET);
ChangeKey (1, true, true, upvec, LC_CK_UP);
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()
{
if (m_nList != 0)
glDeleteLists (m_nList, 1);
delete m_pTarget;
}
void Camera::Initialize()
{
m_fovy = 30;
m_zNear = 1;
m_zFar = 500;
m_pNext = NULL;
m_nState = 0;
m_nList = 0;
m_nType = LC_CAMERA_USER;
m_nList = 0;
m_pTR = NULL;
for (unsigned char i = 0 ; i < sizeof(m_strName) ; i++ )
m_strName[i] = 0;
float *values[] = { m_fEye, m_fTarget, m_fUp };
RegisterKeys (values, camera_key_info, LC_CK_COUNT);
m_pTarget = new CameraTarget (this);
}
/////////////////////////////////////////////////////////////////////////////
// Camera save/load
bool Camera::FileLoad (File& file)
{
unsigned char version, ch;
file.ReadByte (&version, 1);
if (version > LC_CAMERA_SAVE_VERSION)
return false;
if (version > 5)
if (!Object::FileLoad (file))
return false;
if (version == 4)
{
file.Read(m_strName, 80);
m_strName[80] = 0;
}
else
{
file.Read(&ch, 1);
if (ch == 0xFF)
return false; // don't read CString
file.Read(m_strName, ch);
m_strName[ch] = 0;
}
if (version < 3)
{
double d[3];
float f[3];
file.ReadDouble (d, 3);
f[0] = (float)d[0];
f[1] = (float)d[1];
f[2] = (float)d[2];
ChangeKey (1, false, true, f, LC_CK_EYE);
ChangeKey (1, true, true, f, LC_CK_EYE);
file.ReadDouble (d, 3);
f[0] = (float)d[0];
f[1] = (float)d[1];
f[2] = (float)d[2];
ChangeKey (1, false, true, f, LC_CK_TARGET);
ChangeKey (1, true, true, f, LC_CK_TARGET);
file.ReadDouble (d, 3);
f[0] = (float)d[0];
f[1] = (float)d[1];
f[2] = (float)d[2];
ChangeKey (1, false, true, f, LC_CK_UP);
ChangeKey (1, true, true, f, LC_CK_UP);
}
if (version == 3)
{
file.Read(&ch, 1);
while (ch--)
{
unsigned char step;
double eye[3], target[3], up[3];
float f[3];
file.ReadDouble (eye, 3);
file.ReadDouble (target, 3);
file.ReadDouble (up, 3);
file.ReadByte (&step, 1);
if (up[0] == 0 && up[1] == 0 && up[2] == 0)
up[2] = 1;
f[0] = (float)eye[0];
f[1] = (float)eye[1];
f[2] = (float)eye[2];
ChangeKey (step, false, true, f, LC_CK_EYE);
ChangeKey (step, true, true, f, LC_CK_EYE);
f[0] = (float)target[0];
f[1] = (float)target[1];
f[2] = (float)target[2];
ChangeKey (step, false, true, f, LC_CK_TARGET);
ChangeKey (step, true, true, f, LC_CK_TARGET);
f[0] = (float)up[0];
f[1] = (float)up[1];
f[2] = (float)up[2];
ChangeKey (step, false, true, f, LC_CK_UP);
ChangeKey (step, true, true, f, LC_CK_UP);
int snapshot; // BOOL under Windows
int cam;
file.ReadLong (&snapshot, 1);
file.ReadLong (&cam, 1);
// if (cam == -1)
// node->pCam = NULL;
// else
// node->pCam = pDoc->GetCamera(i);
}
}
if (version < 4)
{
double d;
file.ReadDouble (&d, 1); m_fovy = (float)d;
file.ReadDouble (&d, 1); m_zFar = (float)d;
file.ReadDouble (&d, 1); m_zNear= (float)d;
}
else
{
int n;
if (version < 6)
{
unsigned short time;
float param[4];
unsigned char type;
file.ReadLong (&n, 1);
while (n--)
{
file.ReadShort (&time, 1);
file.ReadFloat (param, 3);
file.ReadByte (&type, 1);
ChangeKey (time, false, true, param, type);
}
file.ReadLong (&n, 1);
while (n--)
{
file.ReadShort (&time, 1);
file.ReadFloat (param, 3);
file.ReadByte (&type, 1);
ChangeKey (time, true, true, param, type);
}
}
file.ReadFloat (&m_fovy, 1);
file.ReadFloat (&m_zFar, 1);
file.ReadFloat (&m_zNear, 1);
if (version < 5)
{
file.ReadLong (&n, 1);
if (n != 0)
m_nState |= LC_CAMERA_HIDDEN;
}
else
{
file.ReadByte (&m_nState, 1);
file.ReadByte (&m_nType, 1);
}
}
if ((version > 1) && (version < 4))
{
unsigned long show;
int user;
file.ReadLong (&show, 1);
// if (version > 2)
file.ReadLong (&user, 1);
if (show == 0)
m_nState |= LC_CAMERA_HIDDEN;
}
return true;
}
void Camera::FileSave (File& file) const
{
unsigned char ch = LC_CAMERA_SAVE_VERSION;
file.WriteByte (&ch, 1);
Object::FileSave (file);
ch = (unsigned char)strlen(m_strName);
file.Write (&ch, 1);
file.Write (m_strName, ch);
file.WriteFloat (&m_fovy, 1);
file.WriteFloat (&m_zFar, 1);
file.WriteFloat (&m_zNear, 1);
// version 5
file.WriteByte (&m_nState, 1);
file.WriteByte (&m_nType, 1);
}
/////////////////////////////////////////////////////////////////////////////
// Camera operations
void Camera::Move (unsigned short nTime, bool bAnimation, bool bAddKey, float dx, float dy, float dz)
{
if (IsSide())
{
m_fEye[0] += dx;
m_fEye[1] += dy;
m_fEye[2] += dz;
m_fTarget[0] += dx;
m_fTarget[1] += dy;
m_fTarget[2] += dz;
ChangeKey(nTime, bAnimation, bAddKey, m_fEye, LC_CK_EYE);
ChangeKey(nTime, bAnimation, bAddKey, m_fTarget, LC_CK_TARGET);
}
else
{
if (IsEyeSelected())
{
m_fEye[0] += dx;
m_fEye[1] += dy;
m_fEye[2] += dz;
ChangeKey(nTime, bAnimation, bAddKey, m_fEye, LC_CK_EYE);
}
if (IsTargetSelected())
{
m_fTarget[0] += dx;
m_fTarget[1] += dy;
m_fTarget[2] += dz;
ChangeKey(nTime, bAnimation, bAddKey, m_fTarget, LC_CK_TARGET);
}
// Fix the up vector
Vector upvec(m_fUp), sidevec;
Vector frontvec(m_fTarget[0]-m_fEye[0], m_fTarget[1]-m_fEye[1], m_fTarget[2]-m_fEye[2]);
sidevec.Cross(frontvec, upvec);
upvec.Cross(sidevec, frontvec);
upvec.Normalize();
upvec.ToFloat(m_fUp);
ChangeKey(nTime, bAnimation, bAddKey, m_fUp, LC_CK_UP);
}
}
void Camera::Select (bool bSelecting, bool bFocus, bool bMultiple)
{
if (bSelecting == true)
{
if (bFocus == true)
{
m_nState |= (LC_CAMERA_FOCUSED|LC_CAMERA_SELECTED);
m_pTarget->Select (false, true, bMultiple);
}
else
m_nState |= LC_CAMERA_SELECTED;
if (bMultiple == false)
m_pTarget->Select (false, false, bMultiple);
}
else
{
if (bFocus == true)
m_nState &= ~(LC_CAMERA_FOCUSED);
else
m_nState &= ~(LC_CAMERA_SELECTED|LC_CAMERA_FOCUSED);
}
}
void Camera::SelectTarget (bool bSelecting, bool bFocus, bool bMultiple)
{
// FIXME: the target should handle this
if (bSelecting == true)
{
if (bFocus == true)
{
m_nState |= (LC_CAMERA_TARGET_FOCUSED|LC_CAMERA_TARGET_SELECTED);
Select (false, true, bMultiple);
}
else
m_nState |= LC_CAMERA_TARGET_SELECTED;
if (bMultiple == false)
Select (false, false, bMultiple);
}
else
{
if (bFocus == true)
m_nState &= ~(LC_CAMERA_TARGET_FOCUSED);
else
m_nState &= ~(LC_CAMERA_TARGET_SELECTED|LC_CAMERA_TARGET_FOCUSED);
}
}
void Camera::UpdatePosition(unsigned short nTime, bool bAnimation)
{
CalculateKeys(nTime, bAnimation);
UpdateBoundingBox();
}
void Camera::UpdateBoundingBox()
{
// Fix the up vector
Vector frontvec(m_fEye[0]-m_fTarget[0], m_fEye[1]-m_fTarget[1], m_fEye[2]-m_fTarget[2]);
Vector upvec(m_fUp), sidevec;
sidevec.Cross(frontvec, upvec);
upvec.Cross(sidevec, frontvec);
upvec.Normalize();
upvec.ToFloat(m_fUp);
float len = frontvec.Length();
Matrix mat;
mat.CreateLookat (m_fEye, m_fTarget, m_fUp);
mat.Invert ();
mat.SetTranslation (m_fEye[0], m_fEye[1], m_fEye[2]);
BoundingBoxCalculate (&mat);
mat.SetTranslation (m_fTarget[0], m_fTarget[1], m_fTarget[2]);
m_pTarget->BoundingBoxCalculate (&mat);
mat.SetTranslation (0, 0, 0);
if (!m_nList)
return;
glNewList(m_nList, GL_COMPILE);
glPushMatrix();
glTranslatef(m_fEye[0], m_fEye[1], m_fEye[2]);
glMultMatrixf(mat.m);
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 (m_nTargetList == 0)
{
m_nTargetList = glGenLists(1);
glNewList (m_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)
{
// Create the display lists if this is the first time we're rendered.
if (!m_nList)
{
m_nList = glGenLists(1);
UpdateBoundingBox();
}
if (IsEyeSelected())
{
glLineWidth(fLineWidth*2);
glColor3ubv(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);
glColor3ubv(FlatColorArray[(m_nState & LC_CAMERA_TARGET_FOCUSED) != 0 ? LC_COL_FOCUSED : LC_COL_SELECTED]);
glCallList(m_nTargetList);
glLineWidth(fLineWidth);
}
else
{
glColor3f(0.5f, 0.8f, 0.5f);
glCallList(m_nTargetList);
}
glColor3f(0.5f, 0.8f, 0.5f);
glBegin(GL_LINES);
glVertex3fv(m_fEye);
glVertex3fv(m_fTarget);
glEnd();
if (IsSelected())
{
Matrix projection, modelview;
Vector frontvec(m_fTarget[0]-m_fEye[0], m_fTarget[1]-m_fEye[1], m_fTarget[2]-m_fEye[2]);
float len = frontvec.Length();
glPushMatrix ();
modelview.CreateLookat (m_fEye, m_fTarget, m_fUp);
modelview.Invert ();
glMultMatrixf (modelview.m);
projection.CreatePerspective (m_fovy, 1.33f, 0.01f, len);
projection.Invert ();
glMultMatrixf (projection.m);
// 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(LC_CLICKLINE* pLine)
{
float dist;
if (m_nState & LC_CAMERA_HIDDEN)
return;
dist = (float)BoundingBoxIntersectDist (pLine);
if (dist < pLine->mindist)
{
pLine->mindist = dist;
pLine->pClosest = this;
}
m_pTarget->MinIntersectDist (pLine);
}
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[0];
m_fEye[1] += frontvec[1];
m_fEye[2] += frontvec[2];
m_fTarget[0] += frontvec[0];
m_fTarget[1] += frontvec[1];
m_fTarget[2] += frontvec[2];
ChangeKey(nTime, bAnimation, bAddKey, m_fEye, LC_CK_EYE);
ChangeKey(nTime, bAnimation, bAddKey, m_fTarget, LC_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[0] + sidevec[0];
m_fEye[1] += upvec[1] + sidevec[1];
m_fEye[2] += upvec[2] + sidevec[2];
m_fTarget[0] += upvec[0] + sidevec[0];
m_fTarget[1] += upvec[1] + sidevec[1];
m_fTarget[2] += upvec[2] + sidevec[2];
ChangeKey(nTime, bAnimation, bAddKey, m_fEye, LC_CK_EYE);
ChangeKey(nTime, bAnimation, bAddKey, m_fTarget, LC_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 += Vector(upvec[0] + sidevec[0], upvec[1] + sidevec[1], upvec[2] + sidevec[2]);
frontvec.Normalize();
frontvec *= len;
frontvec += Vector(m_fTarget);
frontvec.ToFloat(m_fEye);
// Calculate new up
upvec = Vector(m_fUp[0], m_fUp[1], m_fUp[2]);
frontvec = Vector(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, LC_CK_EYE);
ChangeKey(nTime, bAnimation, bAddKey, m_fUp, LC_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, LC_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;
}

195
common/camera.h Normal file
View file

@ -0,0 +1,195 @@
#ifndef _CAMERA_H_
#define _CAMERA_H_
#include "opengl.h"
#include "object.h"
#include "algebra.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
class Camera;
class CameraTarget;
class File;
class TiledRender;
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
{
LC_CK_EYE,
LC_CK_TARGET,
LC_CK_UP,
LC_CK_COUNT
} LC_CK_TYPES;
class CameraTarget : public Object
{
public:
CameraTarget (Camera *pParent);
virtual ~CameraTarget ();
public:
void MinIntersectDist (LC_CLICKLINE* pLine);
bool IntersectsVolume(const Vector4* Planes, int NumPlanes)
{ return false; }
void Select (bool bSelecting, bool bFocus, bool bMultiple);
void Move (unsigned short nTime, bool bAnimation, bool bAddKey, float x, float y, float z)
{
// FIXME: move the position handling to the camera target
}
const char* GetName() const;
Camera* GetParent () const
{ return m_pParent; }
protected:
Camera* m_pParent;
friend class Camera; // FIXME: needed for BoundingBoxCalculate ()
// remove and use UpdatePosition instead
};
class Camera : public Object
{
public:
Camera ();
Camera (unsigned char nType, Camera* pPrev);
Camera (float ex, float ey, float ez, float tx, float ty, float tz, Camera* pCamera);
Camera (const float *eye, const float *target, const float *up, Camera* pCamera);
virtual ~Camera ();
// Query functions.
inline Vector3 GetEyePosition() const
{ return Vector3(m_fEye[0], m_fEye[1], m_fEye[2]); };
inline Vector3 GetTargetPosition() const
{ return Vector3(m_fTarget[0], m_fTarget[1], m_fTarget[2]); };
inline Vector3 GetUpVector() const
{ return Vector3(m_fUp[0], m_fUp[1], m_fUp[2]); };
const char* GetName() const
{ return m_strName; };
CameraTarget* GetTarget () const
{ return m_pTarget; }
// Deprecated functions:
const float* GetEyePos () const
{ return m_fEye; };
void GetEyePos (float* eye) const
{ memcpy(eye, m_fEye, sizeof(m_fEye)); };
const float* GetTargetPos () const
{ return m_fTarget; };
void GetTargetPos (float* target) const
{ memcpy(target, m_fTarget, sizeof(m_fTarget)); };
const float* GetUpVec () const
{ return m_fUp; };
void GetUpVec (float* up) const
{ memcpy(up, m_fUp, sizeof(m_fUp)); };
public:
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; }
bool IsEyeFocused()
{ return (m_nState & LC_CAMERA_FOCUSED) != 0; }
bool IsTargetFocused()
{ return (m_nState & LC_CAMERA_TARGET_FOCUSED) != 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); }
void FocusEye()
{ m_nState |= (LC_CAMERA_FOCUSED|LC_CAMERA_SELECTED); }
void FocusTarget()
{ m_nState |= (LC_CAMERA_TARGET_FOCUSED|LC_CAMERA_TARGET_SELECTED); }
*/
void SelectTarget (bool bSelecting, bool bFocus, bool bMultiple);
public:
bool FileLoad (File& file);
void FileSave (File& file) const;
void MinIntersectDist (LC_CLICKLINE* pLine);
void Select (bool bSelecting, bool bFocus, bool bMultiple);
bool IntersectsVolume(const Vector4* Planes, int NumPlanes)
{ return false; }
void UpdatePosition(unsigned short nTime, bool bAnimation);
void Render(float fLineWidth);
void LoadProjection(float fAspect);
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 Initialize();
void UpdateBoundingBox();
// Camera target
CameraTarget* m_pTarget;
// Attributes
char m_strName[81];
unsigned char m_nState;
unsigned char m_nType;
GLuint m_nList;
static GLuint m_nTargetList;
// Current position and orientation.
float m_fEye[3];
float m_fTarget[3];
float m_fUp[3];
TiledRender* m_pTR;
};
#endif // _CAMERA_H_

93
common/console.cpp Normal file
View file

@ -0,0 +1,93 @@
//
// Debug Console
//
#include <stdarg.h>
#include <stdio.h>
#include "console.h"
Console console;
// ============================================================================
Console::Console ()
{
m_pWindowFunc = NULL;
}
Console::~Console ()
{
}
// ============================================================================
void Console::Print (LC_CONSOLE_LEVEL level, const char* format, ...)
{
char text[512];
va_list args;
va_start (args, format);
vsprintf (text, format, args);
va_end (args);
InternalPrint (level, text);
}
void Console::PrintMisc (const char* format, ...)
{
char text[512];
va_list args;
va_start (args, format);
vsprintf (text, format, args);
va_end (args);
InternalPrint (LC_CONSOLE_MISC, text);
}
void Console::PrintDebug (const char* format, ...)
{
char text[512];
va_list args;
va_start (args, format);
vsprintf (text, format, args);
va_end (args);
InternalPrint (LC_CONSOLE_DEBUG, text);
}
void Console::PrintWarning (const char* format, ...)
{
char text[512];
va_list args;
va_start (args, format);
vsprintf (text, format, args);
va_end (args);
InternalPrint (LC_CONSOLE_WARNING, text);
}
void Console::PrintError (const char* format, ...)
{
char text[512];
va_list args;
va_start (args, format);
vsprintf (text, format, args);
va_end (args);
InternalPrint (LC_CONSOLE_ERROR, text);
}
void Console::InternalPrint (LC_CONSOLE_LEVEL level, const char* text)
{
#ifndef LC_DEBUG
if (level == LC_CONSOLE_DEBUG)
return;
#endif
if (m_pWindowFunc)
(*m_pWindowFunc) (level, text, m_pWindowFuncData);
}

42
common/console.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef _CONSOLE_H_
#define _CONSOLE_H_
typedef enum
{
LC_CONSOLE_ERROR,
LC_CONSOLE_WARNING,
LC_CONSOLE_DEBUG,
LC_CONSOLE_MISC
} LC_CONSOLE_LEVEL;
typedef void (*CONSOLECALLBACK) (LC_CONSOLE_LEVEL level, const char* text, void* user_data);
class Console
{
public:
Console ();
virtual ~Console ();
void Print (LC_CONSOLE_LEVEL level, const char* format, ...);
void PrintMisc (const char* format, ...);
void PrintDebug (const char* format, ...);
void PrintWarning (const char* format, ...);
void PrintError (const char* format, ...);
void SetWindowCallback (CONSOLECALLBACK func, void* data)
{ m_pWindowFunc = func; m_pWindowFuncData = data; };
protected:
void InternalPrint (LC_CONSOLE_LEVEL level, const char* text);
CONSOLECALLBACK m_pWindowFunc;
void* m_pWindowFuncData;
// variables
bool use_tty;
bool use_file;
};
extern Console console;
#endif // _CONSOLE_H_

872
common/curve.cpp Executable file
View file

@ -0,0 +1,872 @@
// Curve class, used to represent all flexible objects
//
#include <stdlib.h>
#include <math.h>
#include "globals.h"
#include "curve.h"
#include "opengl.h"
#include "matrix.h"
#include "vector.h"
#define LC_CURVE_SAVE_VERSION 1 // LeoCAD 0.73
#define LC_CURVE_POINT_SAVE_VERSION 1 // LeoCAD 0.73
GLuint CurvePoint::m_nArrowList = 0;
GLuint CurvePoint::m_nSphereList = 0;
static LC_OBJECT_KEY_INFO curve_point_key_info[LC_CURVE_POINT_KEY_COUNT] =
{
{ "Control Point Position", 3, LC_CURVE_POINT_KEY_POSITION },
{ "Control Point Direction 1", 3, LC_CURVE_POINT_KEY_DIRECTION1 },
{ "Control Point Direction 2", 3, LC_CURVE_POINT_KEY_DIRECTION2 },
{ "Control Point Angle", 1, LC_CURVE_POINT_KEY_ANGLE }
};
// =============================================================================
// CurvePoint class
CurvePoint::CurvePoint (Curve *pParent)
: Object (LC_OBJECT_CURVE_POINT)
{
m_pParent = pParent;
/*
FIXME
strcpy (m_strName, pParent->GetName ());
m_strName[LC_OBJECT_NAME_LEN-8] = '\0';
strcat (m_strName, ".Target");
*/
Initialize ();
}
CurvePoint::CurvePoint (Curve *pParent, const float *pos, const float *dir)
: Object (LC_OBJECT_CURVE_POINT)
{
m_pParent = pParent;
/*
FIXME
strcpy (m_strName, pParent->GetName ());
m_strName[LC_OBJECT_NAME_LEN-8] = '\0';
strcat (m_strName, ".Target");
*/
Initialize ();
float angle[1] = { 0 };
ChangeKey (1, false, true, pos, LC_CURVE_POINT_KEY_POSITION);
ChangeKey (1, false, true, dir, LC_CURVE_POINT_KEY_DIRECTION1);
ChangeKey (1, false, true, dir, LC_CURVE_POINT_KEY_DIRECTION2);
ChangeKey (1, false, true, angle, LC_CURVE_POINT_KEY_ANGLE);
ChangeKey (1, true, true, pos, LC_CURVE_POINT_KEY_POSITION);
ChangeKey (1, true, true, dir, LC_CURVE_POINT_KEY_DIRECTION1);
ChangeKey (1, true, true, dir, LC_CURVE_POINT_KEY_DIRECTION2);
ChangeKey (1, true, true, angle, LC_CURVE_POINT_KEY_ANGLE);
UpdatePosition (1, false);
}
void CurvePoint::Initialize ()
{
if (m_nSphereList == 0)
{
m_nSphereList = glGenLists (1);
glNewList (m_nSphereList, GL_COMPILE);
float radius = 0.2f;
int slices = 6, stacks = 6;
float rho, drho, theta, dtheta;
float x, y, z;
int i, 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();
}
m_nState = LC_CURVE_POINT_CONTINUOUS;
float *values[] = { m_fPos, m_fDir1, m_fDir2, &m_fAngle };
RegisterKeys (values, curve_point_key_info, LC_CURVE_POINT_KEY_COUNT);
}
CurvePoint::~CurvePoint ()
{
}
void CurvePoint::MinIntersectDist (LC_CLICKLINE* pLine)
{
float dist = (float)BoundingBoxIntersectDist (pLine);
if (dist < pLine->mindist)
{
pLine->mindist = dist;
pLine->pClosest = this;
m_nLastHit = 1;
}
m_nLastHit = 0;
// FIXME: check arrows
}
void CurvePoint::UpdatePosition (unsigned short nTime, bool bAnimation)
{
CalculateKeys (nTime, bAnimation);
}
bool CurvePoint::FileLoad (File& file)
{
// FIXME
return true;
}
void CurvePoint::FileSave (File& file) const
{
// FIXME
}
void CurvePoint::Move (unsigned short nTime, bool bAnimation, bool bAddKey, float dx, float dy, float dz)
{
/*
if (m_nState & LC_CURVE_POINT_ARROW1_FOCUS)
if (m_nState & LC_CURVE_POINT_ARROW2_FOCUS)
if (m_nState & LC_CURVE_POINT_CONTINUOUS)
;
*/
}
void CurvePoint::Select (bool bSelecting, bool bFocus, bool bMultiple)
{
// FIXME: select arrows, use m_nLastHit
if (bSelecting == true)
{
m_nState |= LC_CURVE_POINT_SELECTED;
if (bFocus == true)
{
m_nState |= LC_CURVE_POINT_FOCUSED;
m_pParent->DeselectOtherPoints (this, bMultiple);
}
}
else
{
if (bFocus == true)
m_nState &= ~(LC_CURVE_POINT_SELECTED|LC_CURVE_POINT_FOCUSED);
else
m_nState &= ~(LC_CURVE_POINT_SELECTED);
}
}
void CurvePoint::Render (LC_RENDER_INFO* pInfo)
{
if (m_nState & LC_CURVE_POINT_FOCUSED)
glColor3ubv (FlatColorArray[LC_COL_FOCUSED]);
else if (m_nState & LC_CURVE_POINT_SELECTED)
glColor3ubv (FlatColorArray[LC_COL_SELECTED]);
else
glColor3f(0.5f, 0.8f, 0.5f); // FIXME: same as camera color, add to FlatColorArray
// glColor3ub (0, 0, 0); // FIXME: inverse of background
// FIXME: add a new color to the array and change the names from LC_COL to LC_COLOR ?
glPushMatrix ();
glTranslatef (m_fPos[0], m_fPos[1], m_fPos[2]);
glCallList (m_nSphereList);
// FIXME: create and use arrow display list
// if (m_pPoints[i].m_nFlags & LC_CURVE_POINT_FOCUSED)
{
glBegin (GL_LINES);
glVertex3f ( m_fDir1[0]/5, m_fDir1[1]/5, m_fDir1[2]/5);
glVertex3f (-m_fDir2[0]/5, -m_fDir2[1]/5, -m_fDir2[2]/5);
glEnd ();
}
glPopMatrix ();
}
// =============================================================================
// Curve class
Curve::Curve ()
: Object (LC_OBJECT_CURVE)
{
Initialize ();
}
Curve::Curve (PieceInfo *pInfo, const float *pos, unsigned char color)
: Object (LC_OBJECT_CURVE)
{
/*
Initialize ();
// FIXME: set the curve type and length based on the PieceInfo
m_fLength = 5;
m_nCurveType = LC_CURVE_TYPE_HOSE;
m_nColor = color;
float dir[3] = { 0, 20, 0 }, pos2[3] = { pos[0] + m_fLength, pos[1], pos[2] };
m_fUp0[0] = 0;
m_fUp0[1] = 0;
m_fUp0[2] = 1;
CurvePoint *pt;
pt = new CurvePoint (this, pos, dir);
m_Points.Add (pt);
dir[1] = 0;
dir[2] = -5;
pt = new CurvePoint (this, pos2, dir);
m_Points.Add (pt);
pos2[0] += 5;
dir[2] = 5;
pt = new CurvePoint (this, pos2, dir);
m_Points.Add (pt);
UpdatePosition (1, false);
*/
}
Curve::~Curve ()
{
/*
for (int i = 0; i < m_Points.GetSize (); i++)
delete m_Points[i];
glDeleteLists (m_nDisplayList, 1);
*/
}
void Curve::Initialize ()
{
m_nCurveType = (LC_CURVE_TYPE)0;
m_nState = 0;
m_nColor = 0;
m_nDisplayList = glGenLists (1);
}
bool Curve::FileLoad (File& file)
{
// FIXME
return true;
}
void Curve::FileSave (File& file) const
{
// FIXME
}
void Curve::MinIntersectDist (LC_CLICKLINE* pLine)
{
// FIXME
}
void Curve::UpdatePosition (unsigned short nTime, bool bAnimation)
{
for (int i = 0; i < m_Points.GetSize (); i++)
m_Points[i]->UpdatePosition (nTime, bAnimation);
glNewList (m_nDisplayList, GL_COMPILE);
switch (m_nCurveType)
{
case LC_CURVE_TYPE_HOSE:
TesselateHose ();
break;
}
glEndList ();
}
void Curve::Move (unsigned short nTime, bool bAnimation, bool bAddKey, float dx, float dy, float dz)
{
for (int i = 0; i < m_Points.GetSize (); i++)
m_Points[i]->Move (nTime, bAnimation, bAddKey, dx, dy, dz);
}
void Curve::Select (bool bSelecting, bool bFocus, bool bMultiple)
{
if (bSelecting == true)
{
if (bFocus == true)
{
m_nState |= (LC_CURVE_SELECTED|LC_CURVE_FOCUSED);
for (int i = 0; i < m_Points.GetSize (); i++)
m_Points[i]->Select (false, true, bMultiple);
}
else
m_nState |= LC_CURVE_SELECTED;
}
else
{
if (bFocus == true)
m_nState &= ~(LC_CURVE_SELECTED|LC_CURVE_FOCUSED);
else
m_nState &= ~(LC_CURVE_SELECTED);
for (int i = 0; i < m_Points.GetSize (); i++)
m_Points[i]->Select (false, bFocus, bMultiple);
}
}
void Curve::DeselectOtherPoints (CurvePoint *pSender, bool bFocusOnly)
{
CurvePoint *pt;
for (int i = 0; i < m_Points.GetSize (); i++)
{
pt = m_Points[i];
if (pt != pSender)
pt->Select (false, bFocusOnly, true);
}
}
/*
void Curve::TesselateHose ()
{
float x, y, z, t, t2, t3, cx[4], cy[4], cz[4];
const float *p1, *p2, *r1, *r2, *u1, *u2;
glEnableClientState (GL_VERTEX_ARRAY);
for (unsigned int i = 0; i < m_Points.GetSize () - 1; i++)
{
p1 = m_Points[i]->GetPosition ();
p2 = m_Points[i+1]->GetPosition ();
r1 = m_Points[i]->GetDirection1 ();
r2 = m_Points[i+1]->GetDirection2 ();
u1 = m_Points[i]->GetUpVector ();
u2 = m_Points[i+1]->GetUpVector ();
cx[0] = 2*p1[0] - 2*p2[0] + r1[0] + r2[0];
cx[1] = -3*p1[0] + 3*p2[0] - 2*r1[0] - r2[0];
cx[2] = r1[0];
cx[3] = p1[0];
cy[0] = 2*p1[1] - 2*p2[1] + r1[1] + r2[1];
cy[1] = -3*p1[1] + 3*p2[1] - 2*r1[1] - r2[1];
cy[2] = r1[1];
cy[3] = p1[1];
cz[0] = 2*p1[2] - 2*p2[2] + r1[2] + r2[2];
cz[1] = -3*p1[2] + 3*p2[2] - 2*r1[2] - r2[2];
cz[2] = r1[2];
cz[3] = p1[2];
int steps1 = 16, steps2 = 6, j, k;
float* verts = (float*)malloc ((steps1+1) * steps2 * 3 * sizeof (float));
float a, b, c;
float ux, uy, uz;
for (t = 0, j = 0; j <= steps1; j++, t += 1.0f/steps1)
{
t2 = t*t;
t3 = t2*t;
// position
x = cx[0]*t3 + cx[1]*t2 + cx[2]*t + cx[3];
y = cy[0]*t3 + cy[1]*t2 + cy[2]*t + cy[3];
z = cz[0]*t3 + cz[1]*t2 + cz[2]*t + cz[3];
// tangent
a = 3*cx[0]*t2 + 2*cx[1]*t + cx[2];
b = 3*cy[0]*t2 + 2*cy[1]*t + cy[2];
c = 3*cz[0]*t2 + 2*cz[1]*t + cz[2];
// gradient
ux = 6*cx[0]*t + 2*cx[1];
uy = 6*cy[0]*t + 2*cy[1];
uz = 6*cz[0]*t + 2*cz[1];
Vector side, front (a, b, c);
Vector up (ux, uy, uz);
side.Cross (front, up);
up.Cross (side, front);
up.Normalize ();
front.Normalize ();
side.Normalize ();
float f[16];
#define M(row,col) f[col*4+row]
M(0,0) = side[0]; M(0,1) = up[0]; M(0,2) = front[0]; M(0,3) = x;
M(1,0) = side[1]; M(1,1) = up[1]; M(1,2) = front[1]; M(1,3) = y;
M(2,0) = side[2]; M(2,1) = up[2]; M(2,2) = front[2]; M(2,3) = z;
M(3,0) = 0.0; M(3,1) = 0.0; M(3,2) = 0.0; M(3,3) = 1.0;
#undef M
float v[3];
Matrix m;
m.FromFloat (f);
for (int k = 0; k < steps2; k++)
{
float *o = &verts[(j*steps2+k)*3];
v[0] = cos (2.0 * M_PI * k / steps2) * 0.15f;
v[1] = sin (2.0 * M_PI * k / steps2) * 0.15f;
v[2] = 0;
m.TransformPoint (o, v);
glVertex3fv (o);
}
}
GLuint *index = (GLuint*)malloc (2 * (steps2+1) * sizeof (GLuint));
glVertexPointer (3, GL_FLOAT, 0, verts);
for (j = 0; j < steps1; j++)
{
for (k = 0; k < steps2; k++)
{
index[k*2] = j*steps2+k;
index[k*2+1] = (j+1)*steps2+k;
}
index[k*2] = index[0];
index[k*2+1] = index[1];
glDrawElements (GL_TRIANGLE_STRIP, 2*(steps2+1), GL_UNSIGNED_INT, index);
}
free (index);
free (verts);
}
}
*/
void Curve::TesselateHose ()
{
float x, y, z, t, t2, t3, cx[4], cy[4], cz[4];
const float *p1, *p2, *r1, *r2;
float u[3] = { m_fUp0[0], m_fUp0[1], m_fUp0[2] };
int steps1 = 16, steps2 = 6, j, k;
float* verts = (float*)malloc ((steps1+1) * steps2 * 3 * sizeof (float));
float a, b, c;
glEnableClientState (GL_VERTEX_ARRAY);
glVertexPointer (3, GL_FLOAT, 0, verts);
for (int i = 0; i < m_Points.GetSize () - 1; i++)
{
float a1, a2, angle_step; // axial rotation
p1 = m_Points[i]->GetPosition ();
p2 = m_Points[i+1]->GetPosition ();
r1 = m_Points[i]->GetDirection1 ();
r2 = m_Points[i+1]->GetDirection2 ();
a1 = m_Points[i]->GetAngle ();
a2 = m_Points[i+1]->GetAngle ();
angle_step = (a2 - a1) / steps1;
if (fabs (angle_step) < 0.01f)
angle_step = 0;
cx[0] = 2*p1[0] - 2*p2[0] + r1[0] + r2[0];
cx[1] = -3*p1[0] + 3*p2[0] - 2*r1[0] - r2[0];
cx[2] = r1[0];
cx[3] = p1[0];
cy[0] = 2*p1[1] - 2*p2[1] + r1[1] + r2[1];
cy[1] = -3*p1[1] + 3*p2[1] - 2*r1[1] - r2[1];
cy[2] = r1[1];
cy[3] = p1[1];
cz[0] = 2*p1[2] - 2*p2[2] + r1[2] + r2[2];
cz[1] = -3*p1[2] + 3*p2[2] - 2*r1[2] - r2[2];
cz[2] = r1[2];
cz[3] = p1[2];
for (t = 0, j = 0; j <= steps1; j++, t += 1.0f/steps1)
{
t2 = t*t;
t3 = t2*t;
// position
x = cx[0]*t3 + cx[1]*t2 + cx[2]*t + cx[3];
y = cy[0]*t3 + cy[1]*t2 + cy[2]*t + cy[3];
z = cz[0]*t3 + cz[1]*t2 + cz[2]*t + cz[3];
// tangent
a = 3*cx[0]*t2 + 2*cx[1]*t + cx[2];
b = 3*cy[0]*t2 + 2*cy[1]*t + cy[2];
c = 3*cz[0]*t2 + 2*cz[1]*t + cz[2];
Vector side, front (a, b, c);
Vector up (u);
side.Cross (front, up);
up.Cross (side, front);
up.Normalize ();
front.Normalize ();
side.Normalize ();
if (angle_step != 0)
{
Matrix rot;
rot.FromAxisAngle (front, angle_step);
rot.TransformPoint (u, up);
}
else
up.ToFloat (u);
float f[16];
#define M(row,col) f[col*4+row]
M(0,0) = side[0]; M(0,1) = up[0]; M(0,2) = front[0]; M(0,3) = x;
M(1,0) = side[1]; M(1,1) = up[1]; M(1,2) = front[1]; M(1,3) = y;
M(2,0) = side[2]; M(2,1) = up[2]; M(2,2) = front[2]; M(2,3) = z;
M(3,0) = 0.0; M(3,1) = 0.0; M(3,2) = 0.0; M(3,3) = 1.0;
#undef M
float v[3];
Matrix m;
m.FromFloat (f);
for (int k = 0; k < steps2; k++)
{
float *o = &verts[(j*steps2+k)*3];
v[0] = (float)(cos (2.0 * M_PI * k / steps2) * 0.15);
v[1] = (float)(sin (2.0 * M_PI * k / steps2) * 0.15);
v[2] = 0;
m.TransformPoint (o, v);
}
}
GLuint *index = (GLuint*)malloc (2 * (steps2+1) * sizeof (GLuint));
for (j = 0; j < steps1; j++)
{
for (k = 0; k < steps2; k++)
{
index[k*2] = j*steps2+k;
index[k*2+1] = (j+1)*steps2+k;
}
index[k*2] = index[0];
index[k*2+1] = index[1];
glDrawElements (GL_TRIANGLE_STRIP, 2*(steps2+1), GL_UNSIGNED_INT, index);
}
free (index);
}
free (verts);
}
void Curve::Render (LC_RENDER_INFO* pInfo)
{
if ((m_nState & LC_CURVE_HIDDEN) != 0)
return;
// FIXME: create a "set color" function in LC_RENDER_INFO
if (pInfo->lighting)
glColor4ubv (ColorArray[m_nColor]);
else
glColor3ubv (FlatColorArray[m_nColor]);
if (m_nColor > 13 && m_nColor < 22) // FIXME: use a #define
{
if (!pInfo->transparent)
{
pInfo->transparent = true;
glEnable (GL_BLEND);
glDepthMask (GL_FALSE);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
}
else
{
if (pInfo->transparent)
{
pInfo->transparent = false;
glDepthMask (GL_TRUE);
glDisable (GL_BLEND);
}
}
// glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
glCallList (m_nDisplayList);
// if (m_nState & LC_CURVE_SELECTED)
{
// turn off transparency to draw the control points
if (pInfo->transparent)
{
pInfo->transparent = false;
if (pInfo->transparent)
{
glDepthMask (GL_TRUE);
glDisable (GL_BLEND);
}
}
for (int i = 0; i < m_Points.GetSize (); i++)
m_Points[i]->Render (pInfo);
}
/*
if (IsSelected ())
{
for (int i = 0; i < m_nNumPoints; i++)
{
if (m_pPoints[i].m_nFlags & LC_CURVE_POINT_FOCUSED)
glColor3ubv (FlatColorArray[LC_COL_FOCUSED]);
else if (m_pPoints[i].m_nFlags & LC_CURVE_POINT_SELECTED)
glColor3ubv (FlatColorArray[LC_COL_SELECTED]);
else
glColor3f(0.5f, 0.8f, 0.5f); // FIXME: same as camera color, add to FlatColorArray
// glColor3ub (0, 0, 0); // FIXME: inverse of background
// FIXME: add a new color to the array and change the names from LC_COL to LC_COLOR ?
glPushMatrix ();
// RenderSegment (m_pPoints[i].m_fPos, m_pPoints[i+1].m_fPos, m_pSegments[i].m_fR1, m_pSegments[i].m_fR2);
glTranslatef (m_pPoints[i].m_fPos[0], m_pPoints[i].m_fPos[1], m_pPoints[i].m_fPos[2]);
glCallList (m_nSphereList);
if (m_pPoints[i].m_nFlags & LC_CURVE_POINT_FOCUSED)
{
glBegin (GL_LINES);
if (i < m_nNumSegments)
{
glVertex3fv (m_pSegments[i].m_fR1);
glVertex3f (0, 0, 0);
}
else if (i > 0)
{
glVertex3f (-m_pSegments[i-1].m_fR2[0], -m_pSegments[i-1].m_fR2[1], -m_pSegments[i-1].m_fR2[2]);
glVertex3f (0, 0, 0);
}
glEnd ();
}
glPopMatrix ();
}
}
*/
/*
if (m_nFlags & LC_CURVE_LOOP)
{
i = m_nNumPoints - 1;
RenderSegment (m_pPoints[0].pos, m_pPoints[i].pos, m_pPoints[0].normal, m_pPoints[i].normal);
}
*/
}
#if 0
#define LC_CURVE_POINT_RADIUS 0.2f
// =============================================================================
// Static functions
#include <math.h>
/*
// TODO: optimize
static void RenderSegment (float p1[3], float p2[3], float r1[3], float r2[3])
{
float x, y, z, t, t2, t3, cx[4], cy[4], cz[4];
int i;
cx[0] = 2*p1[0] - 2*p2[0] + r1[0] + r2[0];
cx[1] = -3*p1[0] + 3*p2[0] - 2*r1[0] - r2[0];
cx[2] = r1[0];
cx[3] = p1[0];
cy[0] = 2*p1[1] - 2*p2[1] + r1[1] + r2[1];
cy[1] = -3*p1[1] + 3*p2[1] - 2*r1[1] - r2[1];
cy[2] = r1[1];
cy[3] = p1[1];
cz[0] = 2*p1[2] - 2*p2[2] + r1[2] + r2[2];
cz[1] = -3*p1[2] + 3*p2[2] - 2*r1[2] - r2[2];
cz[2] = r1[2];
cz[3] = p1[2];
glColor3f (1,0,0);
glBegin (GL_LINE_STRIP);
glVertex3f (cx[3], cy[3], cz[3]);
for (t = 0, i = 0; i < 10; i++)
{
t += 0.1f;
t2 = t*t;
t3 = t2*t;
x = cx[0]*t3 + cx[1]*t2 + cx[2]*t + cx[3];
y = cy[0]*t3 + cy[1]*t2 + cy[2]*t + cy[3];
z = cz[0]*t3 + cz[1]*t2 + cz[2]*t + cz[3];
glVertex3f (x, y, z);
}
glEnd ();
glColor3f (0,0,0);
float a, b, c;
for (t = 0, i = 0; i <= 10; i++, t += 0.1f)
{
t2 = t*t;
t3 = t2*t;
x = cx[0]*t3 + cx[1]*t2 + cx[2]*t + cx[3];
y = cy[0]*t3 + cy[1]*t2 + cy[2]*t + cy[3];
z = cz[0]*t3 + cz[1]*t2 + cz[2]*t + cz[3];
a = 3*cx[0]*t2 + 2*cx[1]*t + cx[2];
b = 3*cy[0]*t2 + 2*cy[1]*t + cy[2];
c = 3*cz[0]*t2 + 2*cz[1]*t + cz[2];
Vector v1 (0, 0, 1);
Vector v2 (a, b, c);
Vector v3;
v3.Cross (v1, v2);
a = v1.Angle (v2);
float v[3];
v3.ToFloat (v);
Matrix m;
m.FromAxisAngle (v, a);
glBegin (GL_LINE_LOOP);
for (int j = 0; j < 16; j++)
{
float o[3];
v[0] = cos (2.0 * M_PI * j / 16) * 0.15f;
v[1] = sin (2.0 * M_PI * j / 16) * 0.15f;
v[2] = 0;
m.TransformPoint (o, v);
glVertex3f (o[0]+x, o[1]+y, o[2]+z);
}
glEnd ();
}
}
*/
// =============================================================================
// Curve class
void Curve::MinIntersectDist (LC_CLICKLINE* pLine)
{
// FIXME: Curve segments and tangent arrows
for (int i = 0; i < m_nNumPoints; i++)
{
double dist = pLine->PointDistance (m_pPoints[i].m_fPos);
if ((dist < pLine->mindist) && (dist < LC_CURVE_POINT_RADIUS))
{
pLine->mindist = dist;
pLine->pClosest = this;
pLine->pParam = &m_pPoints[i];
}
}
}
void Curve::SetSelection (bool bSelect, void *pParam)
{
Object::SetSelection (bSelect, pParam);
if (pParam != NULL)
{
for (int i = 0; i < m_nNumPoints; i++)
if (&m_pPoints[i] == pParam)
{
if (bSelect)
m_pPoints[i].m_nState |= LC_CURVE_POINT_SELECTED;
else
m_pPoints[i].m_nState &= ~(LC_CURVE_POINT_SELECTED | LC_CURVE_POINT_FOCUSED);
}
}
else
{
for (int i = 0; i < m_nNumPoints; i++)
if (bSelect)
m_pPoints[i].m_nState |= LC_CURVE_POINT_SELECTED;
else
m_pPoints[i].m_nState &= ~(LC_CURVE_POINT_SELECTED | LC_CURVE_POINT_FOCUSED);
}
}
void Curve::SetFocus (bool bFocus, void *pParam)
{
Object::SetFocus (bFocus, pParam);
if (pParam != NULL)
{
for (int i = 0; i < m_nNumPoints; i++)
if (&m_pPoints[i] == pParam)
{
if (bFocus)
m_pPoints[i].m_nState |= (LC_CURVE_POINT_SELECTED | LC_CURVE_POINT_FOCUSED);
else
m_pPoints[i].m_nState &= ~LC_CURVE_POINT_FOCUSED;
}
else
{
m_pPoints[i].m_nState &= ~LC_CURVE_POINT_FOCUSED;
}
}
}
#endif

127
common/curve.h Executable file
View file

@ -0,0 +1,127 @@
#ifndef _CURVE_H_
#define _CURVE_H_
#include "object.h"
#include "opengl.h"
#include "array.h"
class Curve;
class CurvePoint;
class PieceInfo;
//#define LC_CURVE_POINT_HIDDEN 0x01
#define LC_CURVE_POINT_SELECTED 0x02
#define LC_CURVE_POINT_FOCUSED 0x04
#define LC_CURVE_POINT_ARROW1_FOCUSED 0x08
#define LC_CURVE_POINT_ARROW2_FOCUSED 0x10
#define LC_CURVE_POINT_CONTINUOUS 0x20
typedef enum
{
LC_CURVE_POINT_KEY_POSITION,
LC_CURVE_POINT_KEY_DIRECTION1,
LC_CURVE_POINT_KEY_DIRECTION2,
LC_CURVE_POINT_KEY_ANGLE,
LC_CURVE_POINT_KEY_COUNT
} LC_CURVE_POINT_KEY_TYPES;
class CurvePoint : public Object
{
public:
// constructors / destructor
CurvePoint (Curve *pParent);
CurvePoint (Curve *pParent, const float *pos, const float *dir);
virtual ~CurvePoint ();
// object functions
bool FileLoad (File& file);
void FileSave (File& file) const;
void MinIntersectDist (LC_CLICKLINE* pLine);
bool IntersectsVolume(const Vector4* Planes, int NumPlanes)
{ return false; }
void UpdatePosition (unsigned short nTime, bool bAnimation);
void Move (unsigned short nTime, bool bAnimation, bool bAddKey, float dx, float dy, float dz);
void Render (LC_RENDER_INFO* pInfo);
void Select (bool bSelecting, bool bFocus, bool bMultiple);
// query functions
Curve* GetParent () const
{ return m_pParent; }
const float* GetPosition () const
{ return m_fPos; }
const float* GetDirection1 () const
{ return m_fDir1; }
const float* GetDirection2 () const
{ return m_fDir2; }
float GetAngle () const
{ return m_fAngle; }
protected:
void Initialize ();
Curve* m_pParent;
static GLuint m_nArrowList;
static GLuint m_nSphereList;
unsigned char m_nState;
// temporary
float m_fPos[3];
float m_fDir1[3];
float m_fDir2[3];
float m_fAngle;
unsigned char m_nLastHit; // FIXME: create arrow objects, ugly hack for now
};
// =============================================================================
#define LC_CURVE_HIDDEN 0x01
#define LC_CURVE_SELECTED 0x02
#define LC_CURVE_FOCUSED 0x04
#define LC_CURVE_LOOP 0x10
#define LC_CURVE_FIXED_SIZE 0x20
// all the different types of curved objects
typedef enum
{
LC_CURVE_TYPE_HOSE
} LC_CURVE_TYPE;
class Curve : public Object
{
public:
// constructors / destructor
Curve ();
Curve (PieceInfo *pInfo, const float *pos, unsigned char color);
virtual ~Curve ();
// object functions
bool FileLoad (File& file);
void FileSave (File& file) const;
void MinIntersectDist (LC_CLICKLINE* pLine);
void UpdatePosition (unsigned short nTime, bool bAnimation);
void Move (unsigned short nTime, bool bAnimation, bool bAddKey, float dx, float dy, float dz);
void Render (LC_RENDER_INFO* pInfo);
void Select (bool bSelecting, bool bFocus, bool bMultiple);
// implementation
void DeselectOtherPoints (CurvePoint *pSender, bool bFocusOnly);
protected:
void Initialize ();
void TesselateHose ();
LC_CURVE_TYPE m_nCurveType;
unsigned char m_nState;
unsigned char m_nColor;
float m_fLength;
float m_fUp0[3];
GLuint m_nDisplayList;
PtrArray<CurvePoint> m_Points;
};
#endif // _CURVE_H_

100
common/debug.cpp Normal file
View file

@ -0,0 +1,100 @@
#include "opengl.h"
#include "debug.h"
#ifdef LC_DEBUG
#define LC_MAX_DEBUG_LINES 100
typedef struct
{
Vector3 pt1;
Vector3 pt2;
Vector3 color;
} LC_DEBUG_LINE;
static LC_DEBUG_LINE DebugLines[LC_MAX_DEBUG_LINES];
static int NumDebugLines;
void ClearDebugLines()
{
NumDebugLines = 0;
}
void AddDebugLine(const Vector3& pt1, const Vector3& pt2, const Vector3& Color)
{
if (NumDebugLines == LC_MAX_DEBUG_LINES-1)
return;
DebugLines[NumDebugLines].pt1 = pt1;
DebugLines[NumDebugLines].pt2 = pt2;
DebugLines[NumDebugLines].color = Color;
NumDebugLines++;
}
#define LC_MAX_DEBUG_QUADS 100
typedef struct
{
Vector3 pt1;
Vector3 pt2;
Vector3 pt3;
Vector3 pt4;
Vector4 color;
} LC_DEBUG_QUAD;
static LC_DEBUG_QUAD DebugQuads[LC_MAX_DEBUG_QUADS];
static int NumDebugQuads;
void ClearDebugQuads()
{
NumDebugQuads = 0;
}
void AddDebugQuad(const Vector3& pt1, const Vector3& pt2, const Vector3& pt3, const Vector3& pt4, const Vector4& Color)
{
if (NumDebugQuads == LC_MAX_DEBUG_QUADS-1)
return;
DebugQuads[NumDebugQuads].pt1 = pt1;
DebugQuads[NumDebugQuads].pt2 = pt2;
DebugQuads[NumDebugQuads].pt3 = pt3;
DebugQuads[NumDebugQuads].pt4 = pt4;
DebugQuads[NumDebugQuads].color = Color;
NumDebugQuads++;
}
void RenderDebugPrimitives()
{
glBegin(GL_LINES);
for (int i = 0; i < NumDebugLines; i++)
{
glColor3fv((float*)&DebugLines[i].color);
glVertex3fv((float*)&DebugLines[i].pt1);
glVertex3fv((float*)&DebugLines[i].pt2);
}
glEnd();
glDepthMask(GL_FALSE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glBegin(GL_QUADS);
for (int i = 0; i < NumDebugQuads; i++)
{
glColor4fv((float*)&DebugQuads[i].color);
glVertex3fv((float*)&DebugQuads[i].pt1);
glVertex3fv((float*)&DebugQuads[i].pt2);
glVertex3fv((float*)&DebugQuads[i].pt3);
glVertex3fv((float*)&DebugQuads[i].pt4);
}
glEnd();
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
}
#endif // LC_DEBUG

17
common/debug.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef _DEBUG_H_
#define _DEBUG_H_
#ifdef LC_DEBUG
#include "algebra.h"
void RenderDebugPrimitives();
void AddDebugLine(const Vector3& pt1, const Vector3& pt2, const Vector3& Color);
void ClearDebugLines();
void AddDebugQuad(const Vector3& pt1, const Vector3& pt2, const Vector3& pt3, const Vector3& pt4, const Vector4& Color);
void ClearDebugQuads();
#endif // LC_DEBUG
#endif // _DEBUG_H_

218
common/defines.h Normal file
View file

@ -0,0 +1,218 @@
// Constant definitions.
//
#ifndef _DEFINES_H_
#define _DEFINES_H_
// Check for supported platforms.
#if !(defined(LC_WINDOWS) || defined(LC_LINUX) || defined(LC_MACOSX))
#error YOU NEED TO DEFINE YOUR OS
#endif
// ============================================================================
// Old defines (mostly deprecated).
#ifdef LC_WINDOWS
#define LC_MAXPATH 260 //_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 LC_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);
int stricmp(const char* str1, const char* str2);
#endif
#ifdef LC_MACOSX
#include <sys/param.h>
#define LC_MAXPATH MAXPATHLEN //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);
int stricmp(const char* str1, const char* str2);
#endif
/////////////////////////////////////////////////////////////////////////////
// LeoCAD constants
// Math numbers.
#define LC_DTOR 0.017453f
#define LC_RTOD 57.29578f
#define LC_PI 3.141592f
#define LC_2PI 6.283185f
#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 LC_WINDOWS
#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)
#ifndef M_PI
#define M_PI 3.14159265
#endif
#define LC_FOURCC(ch0, ch1, ch2, ch3) (lcuint32)((lcuint32)(lcuint8)(ch0) | ((lcuint32)(lcuint8)(ch1) << 8) | \
((lcuint32)(lcuint8)(ch2) << 16) | ((lcuint32)(lcuint8)(ch3) << 24 ))
#define LC_FILE_ID LC_FOURCC('L','C','D', 0)
#define LC_CONNECTIONS 2 // Different piece connections
#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 LC_DRAW_SNAP_XYZ (LC_DRAW_SNAP_X | LC_DRAW_SNAP_Y | LC_DRAW_SNAP_Z)
#define LC_DRAW_GLOBAL_SNAP 0x0040 // Don't allow relative snap.
#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_LOCK_XYZ (LC_DRAW_LOCK_X | LC_DRAW_LOCK_Y | LC_DRAW_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
// HTML export options
#define LC_HTML_SINGLEPAGE 0x01
#define LC_HTML_INDEX 0x02
#define LC_HTML_IMAGES 0x04
#define LC_HTML_LISTEND 0x08
#define LC_HTML_LISTSTEP 0x10
#define LC_HTML_HIGHLIGHT 0x20
#define LC_HTML_HTMLEXT 0x40
// Piece library update
#define LC_UPDATE_DELETE 0x00
#define LC_UPDATE_DESCRIPTION 0x01
#define LC_UPDATE_DRAWINFO 0x02
#define LC_UPDATE_NEWPIECE 0x04
#endif // _DEFINES_H_

571
common/file.cpp Normal file
View file

@ -0,0 +1,571 @@
// File class, can be a memory file or in the disk.
// Needed to work with the clipboard and undo/redo easily.
// NOTE: Because of endianess issues, all I/O must be done from a File class.
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include <string.h>
#include "file.h"
#include "defines.h"
#include "config.h"
#include "str.h"
// =============================================================================
// File construction/destruction
File::File ()
{
strcpy(FileName, "");
}
File::~File ()
{
}
// =============================================================================
// Endian-safe functions
// reads 1-byte integers
unsigned long File::ReadByte (void* pBuf, unsigned long nCount)
{
return Read (pBuf, nCount);
}
// reads 2-byte integers
unsigned long File::ReadShort (void* pBuf, unsigned long nCount)
{
unsigned long read;
read = Read (pBuf, nCount*2)/2;
#ifdef LC_BIG_ENDIAN
unsigned long i;
lcuint16* val = (lcuint16*)pBuf, x;
for (i = 0; i < read; i++)
{
x = *val;
*val = ((x>>8) | (x<<8));
val++;
}
#endif
return read;
}
// reads 4-byte integers
unsigned long File::ReadLong (void* pBuf, unsigned long nCount)
{
unsigned long read;
read = Read (pBuf, nCount*4)/4;
#ifdef LC_BIG_ENDIAN
unsigned long i;
lcuint32* val = (lcuint32*)pBuf, x;
for (i = 0; i < read; i++)
{
x = *val;
*val = ((x>>24) | ((x>>8) & 0xff00) | ((x<<8) & 0xff0000) | (x<<24));
val++;
}
#endif
return read;
}
// reads 4-byte floats
unsigned long File::ReadFloat (void* pBuf, unsigned long nCount)
{
unsigned long read;
read = Read (pBuf, nCount*4)/4;
#ifdef LC_BIG_ENDIAN
unsigned long i;
float* val = (float*)pBuf;
union { unsigned char b[4]; float f; } in, out;
for (i = 0; i < read; i++)
{
in.f = *val;
out.b[0] = in.b[3];
out.b[1] = in.b[2];
out.b[2] = in.b[1];
out.b[3] = in.b[0];
*val = out.f;
val++;
}
#endif
return read;
}
// reads 8-byte floats
unsigned long File::ReadDouble (void* pBuf, unsigned long nCount)
{
unsigned long read;
read = Read (pBuf, nCount*8)/8;
#ifdef LC_BIG_ENDIAN
unsigned long i;
double* val = (double*)pBuf;
union { unsigned char b[8]; double d; } in, out;
for (i = 0; i < read; i++)
{
in.d = *val;
out.b[0] = in.b[7];
out.b[1] = in.b[6];
out.b[2] = in.b[5];
out.b[3] = in.b[4];
out.b[4] = in.b[3];
out.b[5] = in.b[2];
out.b[6] = in.b[1];
out.b[7] = in.b[0];
*val = out.d;
val++;
}
#endif
return read;
}
// writes 1-byte integers
unsigned long File::WriteByte (const void* pBuf, unsigned long nCount)
{
return Write (pBuf, nCount);
}
// writes 2-byte integers
unsigned long File::WriteShort (const void* pBuf, unsigned long nCount)
{
#ifdef LC_BIG_ENDIAN
unsigned long wrote = 0, i;
lcuint16* val = (lcuint16*)pBuf, x;
for (i = 0; i < nCount; i++)
{
x = (((*val)>>8) | ((*val)<<8));
val++;
wrote += Write (&x, 2)/2;
}
return wrote;
#else
return Write(pBuf, nCount*2)/2;
#endif
}
// writes 4-byte integers
unsigned long File::WriteLong (const void* pBuf, unsigned long nCount)
{
#ifdef LC_BIG_ENDIAN
unsigned long wrote = 0, i;
lcuint32* val = (lcuint32*)pBuf, x;
for (i = 0; i < nCount; i++)
{
x = (((*val)>>24) | (((*val)>>8) & 0xff00) | (((*val)<<8) & 0xff0000) | ((*val)<<24));
val++;
wrote += Write (&x, 4)/4;
}
return wrote;
#else
return Write (pBuf, nCount*4)/4;
#endif
}
// writes 4-byte floats
unsigned long File::WriteFloat (const void* pBuf, unsigned long nCount)
{
#ifdef LC_BIG_ENDIAN
unsigned long wrote = 0, i;
float* val = (float*)pBuf, x;
union { unsigned char b[4]; float f; } in, out;
for (i = 0; i < nCount; i++)
{
in.f = *val;
val++;
out.b[0] = in.b[3];
out.b[1] = in.b[2];
out.b[2] = in.b[1];
out.b[3] = in.b[0];
x = out.f;
wrote += Write (&x, 4)/4;
}
return wrote;
#else
return Write (pBuf, nCount*4)/4;
#endif
}
// writes 8-byte floats
unsigned long File::WriteDouble (const void* pBuf, unsigned long nCount)
{
#ifdef LC_BIG_ENDIAN
unsigned long wrote = 0, i;
double* val = (double*)pBuf, x;
union { unsigned char b[8]; double d; } in, out;
for (i = 0; i < nCount; i++)
{
in.d = *val;
val++;
out.b[0] = in.b[7];
out.b[1] = in.b[6];
out.b[2] = in.b[5];
out.b[3] = in.b[4];
out.b[4] = in.b[3];
out.b[5] = in.b[2];
out.b[6] = in.b[1];
out.b[7] = in.b[0];
x = out.d;
wrote += Write (&x, 8)/8;
}
return wrote;
#else
return Write (pBuf, nCount*8)/8;
#endif
}
void File::ReadString(String& Value)
{
lcuint32 l;
ReadInt(&l);
Read(Value.GetBuffer(l+1), l);
((char*)Value)[l] = 0;
}
void File::WriteString(const String& Value)
{
WriteInt(Value.GetLength());
Write((const char*)Value, Value.GetLength());
}
// =============================================================================
FileMem::FileMem()
{
m_nGrowBytes = 1024;
m_nPosition = 0;
m_nBufferSize = 0;
m_nFileSize = 0;
m_pBuffer = NULL;
m_bAutoDelete = true;
}
FileDisk::FileDisk()
{
m_hFile = NULL;
m_bCloseOnDelete = false;
}
FileMem::~FileMem()
{
if (m_pBuffer)
Close();
m_nGrowBytes = 0;
m_nPosition = 0;
m_nBufferSize = 0;
m_nFileSize = 0;
}
FileDisk::~FileDisk()
{
if (m_hFile != NULL && m_bCloseOnDelete)
Close();
}
/////////////////////////////////////////////////////////////////////////////
// File operations
char* FileMem::ReadLine(char* pBuf, unsigned long nMax)
{
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;
}
char* FileDisk::ReadLine(char* pBuf, unsigned long nMax)
{
return fgets(pBuf, nMax, m_hFile);
}
unsigned long FileMem::Read(void* pBuf, unsigned long nCount)
{
if (nCount == 0)
return 0;
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;
}
unsigned long FileDisk::Read(void* pBuf, unsigned long nCount)
{
return fread(pBuf, 1, nCount, m_hFile);
}
int FileMem::GetChar()
{
if (m_nPosition > m_nFileSize)
return EOF;
unsigned char* ret = (unsigned char*)m_pBuffer + m_nPosition;
m_nPosition++;
return *ret;
}
int FileDisk::GetChar()
{
return fgetc(m_hFile);
}
unsigned long FileMem::Write(const void* pBuf, unsigned long nCount)
{
if (nCount == 0)
return 0;
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;
}
unsigned long FileDisk::Write(const void* pBuf, unsigned long nCount)
{
return fwrite(pBuf, 1, nCount, m_hFile);
}
int FileMem::PutChar(int c)
{
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;
}
int FileDisk::PutChar(int c)
{
return fputc(c, m_hFile);
}
bool FileDisk::Open(const char *filename, const char *mode)
{
if (*filename == 0)
return false;
strcpy(FileName, filename);
m_hFile = fopen(filename, mode);
m_bCloseOnDelete = true;
return (m_hFile != NULL);
}
void FileMem::Close()
{
m_nGrowBytes = 0;
m_nPosition = 0;
m_nBufferSize = 0;
m_nFileSize = 0;
if (m_pBuffer && m_bAutoDelete)
free(m_pBuffer);
m_pBuffer = NULL;
strcpy(FileName, "");
}
void FileDisk::Close()
{
if (m_hFile != NULL)
fclose(m_hFile);
m_hFile = NULL;
m_bCloseOnDelete = false;
strcpy(FileName, "");
}
unsigned long FileMem::Seek(long lOff, int nFrom)
{
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 0;
}
unsigned long FileDisk::Seek(long lOff, int nFrom)
{
fseek (m_hFile, lOff, nFrom);
return ftell(m_hFile);
}
unsigned long FileMem::GetPosition() const
{
return m_nPosition;
}
unsigned long FileDisk::GetPosition() const
{
return ftell(m_hFile);
}
void FileMem::GrowFile(unsigned long nNewLen)
{
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 FileMem::Flush()
{
// Nothing to be done
}
void FileDisk::Flush()
{
if (m_hFile == NULL)
return;
fflush(m_hFile);
}
void FileMem::Abort()
{
Close();
}
void FileDisk::Abort()
{
if (m_hFile != NULL)
{
// close but ignore errors
if (m_bCloseOnDelete)
fclose(m_hFile);
m_hFile = NULL;
m_bCloseOnDelete = false;
}
}
void FileMem::SetLength(unsigned long nNewLen)
{
if (nNewLen > m_nBufferSize)
GrowFile(nNewLen);
if (nNewLen < m_nPosition)
m_nPosition = nNewLen;
m_nFileSize = nNewLen;
}
void FileDisk::SetLength(unsigned long nNewLen)
{
fseek(m_hFile, nNewLen, SEEK_SET);
}
unsigned long FileMem::GetLength() const
{
return m_nFileSize;
}
unsigned long FileDisk::GetLength() const
{
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;
}

140
common/file.h Normal file
View file

@ -0,0 +1,140 @@
#ifndef _FILE_H_
#define _FILE_H_
#include <stdio.h>
#include <string.h>
#include "defines.h"
#include "config.h"
class String;
class File
{
public:
// Constructors
File();
virtual ~File();
// Implementation
public:
virtual unsigned long GetPosition() const = 0;
virtual unsigned long Seek(long lOff, int nFrom) = 0;
virtual void SetLength(unsigned long nNewLen) = 0;
virtual unsigned long GetLength() const = 0;
virtual char* ReadLine(char* pBuf, unsigned long nMax)=0;
virtual unsigned long Read(void* pBuf, unsigned long nCount)=0;
virtual unsigned long Write(const void* pBuf, unsigned long nCount)=0;
virtual int GetChar()=0;
virtual int PutChar(int c)=0;
unsigned long ReadByte(void* pBuf, unsigned long nCount);
unsigned long ReadShort(void* pBuf, unsigned long nCount);
unsigned long ReadLong(void* pBuf, unsigned long nCount);
unsigned long ReadFloat(void* pBuf, unsigned long nCount);
unsigned long ReadDouble(void* pBuf, unsigned long nCount);
unsigned long WriteByte(const void* pBuf, unsigned long nCount);
unsigned long WriteShort(const void* pBuf, unsigned long nCount);
unsigned long WriteLong(const void* pBuf, unsigned long nCount);
unsigned long WriteFloat(const void* pBuf, unsigned long nCount);
unsigned long WriteDouble(const void* pBuf, unsigned long nCount);
void ReadString(String& Value);
void ReadInt(lcint32* Value)
{ ReadLong(Value, 1); }
void ReadInt(lcuint32* Value)
{ ReadLong(Value, 1); }
void WriteString(const String& Value);
void WriteInt(lcint32 Value)
{ WriteLong(&Value, 1); }
void WriteInt(lcuint32 Value)
{ WriteLong(&Value, 1); }
virtual void Abort()=0;
virtual void Flush()=0;
virtual void Close()=0;
const char* GetFileName() const
{ return FileName; }
void SetFileName(const char* Name)
{ strncpy(FileName, Name, LC_MAXPATH); }
protected:
char FileName[LC_MAXPATH];
};
class FileMem : public File
{
public:
// Constructors
FileMem();
~FileMem();
// Implementation
public:
unsigned long GetPosition() const;
unsigned long Seek(long lOff, int nFrom);
void SetLength(unsigned long nNewLen);
unsigned long GetLength() const;
char* ReadLine(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);
void* GetBuffer() const
{
return m_pBuffer;
}
protected:
// 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);
};
class FileDisk : public File
{
public:
// Constructors
FileDisk();
~FileDisk();
// Implementation
public:
unsigned long GetPosition() const;
unsigned long Seek(long lOff, int nFrom);
void SetLength(unsigned long nNewLen);
unsigned long GetLength() const;
char* ReadLine(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);
protected:
// DiscFile specific:
FILE* m_hFile;
bool m_bCloseOnDelete;
};
#endif // _FILE_H_

89
common/globals.cpp Normal file
View file

@ -0,0 +1,89 @@
//
// Global variables common to all platforms.
//
#include <stdlib.h>
#include "globals.h"
Messenger* messenger;
MainWnd* main_window;
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" };
unsigned char FlatColorArray[31][3] = {
{ 166, 25, 25 }, // 0 - Red
{ 255, 127, 51 }, // 1 - Orange
{ 25, 102, 25 }, // 2 - Green
{ 76, 153, 76 }, // 3 - Light Green
{ 0, 51, 178 }, // 4 - Blue
{ 51, 102, 229 }, // 5 - Light Blue
{ 204, 204, 0 }, // 6 - Yellow
{ 242, 242, 242 }, // 7 - White
{ 84, 76, 76 }, // 8 - Dark Gray
{ 51, 51, 51 }, // 9 - Black
{ 102, 51, 51 }, //10 - Brown
{ 178, 76, 153 }, //11 - Pink
{ 153, 51, 153 }, //12 - Purple
{ 229, 178, 51 }, //13 - Gold
{ 153, 25, 25 }, //14 - Clear Red
{ 255, 153, 76 }, //15 - Clear Orange
{ 25, 102, 25 }, //16 - Clear Green
{ 153, 178, 76 }, //17 - Clear Light Green
{ 0, 0, 127 }, //18 - Clear Blue
{ 51, 102, 229 }, //19 - Clear Light Blue
{ 229, 229, 0 }, //20 - Clear Yellow
{ 229, 229, 229 }, //21 - Clear White
{ 140, 140, 140 }, //22 - Light Gray
{ 204, 204, 178 }, //23 - Tan
{ 153, 102, 102 }, //24 - Light Brown
{ 229, 178, 229 }, //25 - Light Pink
{ 25, 178, 204 }, //26 - Turquoise
{ 204, 204, 204 }, //27 - Silver
{ 0, 0, 0 }, //28 - Edges
{ 229, 76, 102 }, //29 - Selected
{ 102, 76, 229 }}; //30 - Focused
unsigned char ColorArray[31][4] = {
{ 166, 25, 25, 255 }, // 0 - Red
{ 255, 127, 51, 255 }, // 1 - Orange
{ 25, 102, 25, 255 }, // 2 - Green
{ 76, 153, 76, 255 }, // 3 - Light Green
{ 0, 51, 178, 255 }, // 4 - Blue
{ 51, 102, 229, 255 }, // 5 - Light Blue
{ 204, 204, 0, 255 }, // 6 - Yellow
{ 242, 242, 242, 255 }, // 7 - White
{ 76, 76, 76, 255 }, // 8 - Dark Gray
{ 25, 25, 25, 255 }, // 9 - Black
{ 102, 51, 51, 255 }, //10 - Brown
{ 178, 76, 153, 255 }, //11 - Pink
{ 153, 51, 153, 255 }, //12 - Purple
{ 229, 178, 51, 255 }, //13 - Gold
{ 153, 25, 25, 153 }, //14 - Clear Red
{ 255, 153, 76, 153 }, //15 - Clear Orange
{ 25, 102, 25, 153 }, //16 - Clear Green
{ 153, 178, 76, 153 }, //17 - Clear Light Green
{ 0, 0, 127, 153 }, //18 - Clear Blue
{ 51, 102, 229, 153 }, //19 - Clear Light Blue
{ 229, 229, 0, 153 }, //20 - Clear Yellow
{ 229, 229, 229, 153 }, //21 - Clear White
{ 127, 127, 127, 255 }, //22 - Light Gray
{ 204, 204, 178, 255 }, //23 - Tan
{ 153, 102, 102, 255 }, //24 - Light Brown
{ 229, 178, 229, 255 }, //25 - Light Pink
{ 25, 178, 204, 255 }, //26 - Turquoise
{ 204, 204, 204, 255 }, //27 - Silver
{ 51, 51, 51, 255 }, //28 - Edges
{ 229, 76, 102, 255 }, //29 - Selected
{ 102, 76, 229, 255 }}; //30 - Focused

19
common/globals.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef _GLOBALS_H_
#define _GLOBALS_H_
#include "defines.h"
#include "typedefs.h"
#include "console.h"
class Messenger;
extern Messenger* messenger;
class MainWnd;
extern MainWnd* main_window;
extern unsigned char FlatColorArray[31][3];
extern unsigned char ColorArray[31][4];
extern const char* colornames[LC_MAXCOLORS];
extern const char* altcolornames[LC_MAXCOLORS];
#endif // _GLOBALS_H_

52
common/glwindow.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef _GLWINDOW_H_
#define _GLWINDOW_H_
class GLWindow
{
public:
GLWindow(GLWindow *share);
virtual ~GLWindow();
void IncRef()
{ m_nRef++; }
void DecRef()
{ m_nRef--; if (m_nRef == 0) delete this; }
bool Create(void* data);
void DestroyContext();
bool MakeCurrent();
void SwapBuffers();
void Redraw();
void CaptureMouse();
void ReleaseMouse();
int GetWidth() const
{ return m_nWidth; }
int GetHeight() const
{ return m_nHeight; }
void* GetData() const
{ return m_pData; }
virtual void OnDraw() { };
virtual void OnSize(int cx, int cy)
{ m_nWidth = cx; m_nHeight = cy; };
virtual void OnInitialUpdate();
virtual void OnLeftButtonDown(int x, int y, bool bControl, bool bShift) { };
virtual void OnLeftButtonUp(int x, int y, bool bControl, bool bShift) { };
virtual void OnLeftButtonDoubleClick(int x, int y, bool bControl, bool bShift) { };
virtual void OnRightButtonDown(int x, int y, bool bControl, bool bShift) { };
virtual void OnRightButtonUp(int x, int y, bool bControl, bool bShift) { };
virtual void OnMouseMove(int x, int y, bool bControl, bool bShift) { };
protected:
int m_nWidth;
int m_nHeight;
private:
void *m_pData;
GLWindow *m_pShare;
int m_nRef;
};
#endif // _GLWINDOW_H_

79
common/group.cpp Normal file
View file

@ -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->ReadLong(&i, 1);
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->WriteLong(&i, 1);
}

31
common/group.h Normal file
View file

@ -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

492
common/im_bmp.cpp Executable file
View file

@ -0,0 +1,492 @@
#include <stdio.h>
#include <stdlib.h>
#include "config.h"
#include "quant.h"
#include "image.h"
#include "file.h"
// ========================================================
bool Image::LoadBMP (File& file)
{
lcint32 bmWidth, bmHeight;
lcuint8 bmPlanes, bmBitsPixel, m1, m2;
typedef struct {
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} RGBQUAD;
lcint16 res1,res2;
lcint32 filesize, pixoff;
lcint32 bmisize, compression;
lcint32 xscale, yscale;
lcint32 colors, impcol, rc;
lcuint32 sizeimage, m_bytesRead = 0;
FreeData ();
if (file.Read (&m1, 1) != 1)
return false;
m_bytesRead++;
if (file.Read (&m2, 1) != 1)
return false;
m_bytesRead++;
if ((m1 != 'B') || (m2 != 'M'))
return false;
rc = file.ReadLong ((long*)&(filesize), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadShort ((int*)&(res1), 1); m_bytesRead+=2;
if (rc != 1) { return false; }
rc = file.ReadShort ((int*)&(res2), 1); m_bytesRead+=2;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(pixoff), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(bmisize), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(bmWidth), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(bmHeight), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadShort ((int*)&(bmPlanes), 1); m_bytesRead+=2;
if (rc != 1) { return false; }
rc = file.ReadShort ((int*)&(bmBitsPixel), 1); m_bytesRead+=2;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(compression), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(sizeimage), 1); m_bytesRead+=4;
if (rc != 1) {return false; }
rc = file.ReadLong ((long*)&(xscale), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(yscale), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(colors), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(impcol), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
if (colors == 0)
colors = 1 << bmBitsPixel;
RGBQUAD *colormap = NULL;
if (bmBitsPixel != 24)
{
colormap = new RGBQUAD[colors];
if (colormap == NULL)
return false;
int i;
for (i = 0; i < colors; i++)
{
unsigned char r ,g, b, dummy;
rc = file.Read (&b, 1);
m_bytesRead++;
if (rc!=1)
{
delete [] colormap;
return false;
}
rc = file.Read (&g, 1);
m_bytesRead++;
if (rc!=1)
{
delete [] colormap;
return false;
}
rc = file.Read (&r, 1);
m_bytesRead++;
if (rc != 1)
{
delete [] colormap;
return false;
}
rc = file.Read (&dummy, 1);
m_bytesRead++;
if (rc != 1)
{
delete [] colormap;
return false;
}
colormap[i].rgbRed = r;
colormap[i].rgbGreen = g;
colormap[i].rgbBlue = b;
}
}
if ((long)m_bytesRead > pixoff)
{
delete [] colormap;
return false;
}
while ((long)m_bytesRead < pixoff)
{
char dummy;
file.Read (&dummy, 1);
m_bytesRead++;
}
int w = bmWidth;
int h = bmHeight;
// set the output params
m_pData = (unsigned char*)malloc (w*h*3);
long row_size = w * 3;
if (m_pData != NULL)
{
m_nWidth = w;
m_nHeight = h;
m_bAlpha = false;
unsigned char* outbuf = m_pData;
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 (file.Read (pixel, 3) ==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 (file.Read (&dummy, 1) != 1)
{
FreeData ();
return false;
}
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 (file.Read (&inbyte, 1) != 1)
{
FreeData ();
delete [] colormap;
return false;
}
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 (file.Read (&dummy, 1) != 1)
{
FreeData ();
if (colormap)
delete [] colormap;
return false;
}
m_bytesRead++;
}
}
}
}
else
{
int i, x = 0;
unsigned char c, c1 = 0, *pp;
row = 0;
pp = outbuf + (bmHeight-1)*bmWidth*3;
if (bmBitsPixel == 8)
{
while (row < bmHeight)
{
c = file.GetChar ();
if (c)
{
// encoded mode
c1 = file.GetChar ();
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 = file.GetChar ();
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 = file.GetChar ();
x += c;
c = file.GetChar ();
row += c;
pp = outbuf + x*3 + (bmHeight-row-1)*bmWidth*3;
}
else // absolute mode
{
for (i = 0; i < c; x++, i++)
{
c1 = file.GetChar ();
*pp = colormap[c1].rgbRed; pp++;
*pp = colormap[c1].rgbGreen; pp++;
*pp = colormap[c1].rgbBlue; pp++;
}
if (c & 1)
file.GetChar (); // odd length run: read an extra pad byte
}
}
}
}
else if (bmBitsPixel == 4)
{
while (row < bmHeight)
{
c = file.GetChar ();
if (c)
{
// encoded mode
c1 = file.GetChar ();
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 = file.GetChar ();
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 = file.GetChar ();
x += c;
c = file.GetChar ();
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 = file.GetChar ();
*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))
file.GetChar (); // odd length run: read an extra pad byte
}
}
}
}
}
if (colormap)
delete [] colormap;
}
return true;
}
// ========================================================
bool Image::SaveBMP (File& file, bool quantize) const
{
unsigned short bits;
unsigned long cmap, bfSize;
unsigned char pal[3][256], *colormappedbuffer = NULL;
if (quantize)
{
colormappedbuffer = (unsigned char*)malloc(m_nWidth*m_nHeight);
dl1quant (m_pData, colormappedbuffer, m_nWidth, m_nHeight, 256, true, pal);
bits = 8;
cmap = 256;
bfSize = 1078 + m_nWidth*m_nHeight;
}
else
{
bits = 24;
cmap = 0;
bfSize = 54 + m_nWidth*m_nHeight*3;
}
long byteswritten = 0;
long pixoff = 54 + cmap*4;
short res = 0;
char m1 ='B', m2 ='M';
file.WriteByte (&m1, 1); byteswritten++; // B
file.WriteByte (&m2, 1); byteswritten++; // M
file.WriteLong (&bfSize, 1); byteswritten+=4;// bfSize
file.WriteShort (&res, 1); byteswritten+=2;// bfReserved1
file.WriteShort (&res, 1); byteswritten+=2;// bfReserved2
file.WriteLong (&pixoff, 1); byteswritten+=4;// bfOffBits
lcuint32 biSize = 40, compress = 0, size = 0;
lcint32 width = m_nWidth, height = m_nHeight, pixels = 0;
lcuint16 planes = 1;
file.WriteLong (&biSize, 1); byteswritten+=4;// biSize
file.WriteLong (&width, 1); byteswritten+=4;// biWidth
file.WriteLong (&height, 1); byteswritten+=4;// biHeight
file.WriteShort (&planes, 1); byteswritten+=2;// biPlanes
file.WriteShort (&bits, 1); byteswritten+=2;// biBitCount
file.WriteLong (&compress, 1); byteswritten+=4;// biCompression
file.WriteLong (&size, 1); byteswritten+=4;// biSizeImage
file.WriteLong (&pixels, 1); byteswritten+=4;// biXPelsPerMeter
file.WriteLong (&pixels, 1); byteswritten+=4;// biYPelsPerMeter
file.WriteLong (&cmap, 1); byteswritten+=4;// biClrUsed
file.WriteLong (&cmap, 1); byteswritten+=4;// biClrImportant
if (quantize)
{
for (int i = 0; i < 256; i++)
{
file.PutChar (pal[2][i]);
file.PutChar (pal[1][i]);
file.PutChar (pal[0][i]);
file.PutChar (0); // dummy
}
for (int row = 0; row < m_nHeight; row++)
{
int pixbuf = 0;
for (int col = 0; col < m_nWidth; col++)
{
int offset = (m_nHeight-row-1) * width + col; // offset into our color-mapped RGB buffer
unsigned char pval = *(colormappedbuffer + offset);
pixbuf = (pixbuf << 8) | pval;
file.PutChar (pixbuf);
pixbuf = 0;
byteswritten++;
}
// DWORD align
while ((byteswritten - pixoff) & 3)
{
file.PutChar (0);
byteswritten++;
}
}
free(colormappedbuffer);
}
else
{
unsigned long widthDW = (((m_nWidth*24) + 31) / 32 * 4);
long row, row_size = m_nWidth*3;
for (row = 0; row < m_nHeight; row++)
{
unsigned char* buf = m_pData+(m_nHeight-row-1)*row_size;
// write a row
for (int col = 0; col < row_size; col += 3)
{
file.PutChar (buf[col+2]);
file.PutChar (buf[col+1]);
file.PutChar (buf[col]);
}
byteswritten += row_size;
for (unsigned long count = row_size; count < widthDW; count++)
{
file.PutChar (0); // dummy
byteswritten++;
}
}
}
return true;
}

699
common/im_gif.cpp Normal file
View file

@ -0,0 +1,699 @@
#include <stdlib.h>
#include <string.h>
#include "image.h"
#include "quant.h"
#include "file.h"
#include "config.h"
// =============================================================================
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
lcuint16 *symbol_head; // => table of prefix symbols
lcuint8 *symbol_tail; // => table of suffix bytes
lcuint8 *symbol_stack; // => stack for symbol expansions
lcuint8 *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;
lcuint32 cur_row_number; // need to know actual row number
lcuint32 pass2_offset; // # of pixel rows in pass 1
lcuint32 pass3_offset; // # of pixel rows in passes 1&2
lcuint32 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 lcint32 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++) = (lcuint8) 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] = (lcuint8) 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
}
bool Image::LoadGIF (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;
FreeData ();
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 false;
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;
char buf[256];
extlabel = source->input_file->GetChar();
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 = (lcuint16*) malloc(LZW_TABLE_SIZE * sizeof(lcuint16));
source->symbol_tail = (lcuint8*) malloc (LZW_TABLE_SIZE * sizeof(lcuint8));
source->symbol_stack = (lcuint8*) malloc (LZW_TABLE_SIZE * sizeof(lcuint8));
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);
m_pData = (unsigned char*)malloc(width*height*3);
m_nWidth = width;
m_nHeight = height;
m_bAlpha = false; // FIXME: create the alpha channel for transparent files
unsigned char* buf = m_pData;
for (unsigned long scanline = 0; scanline < height; scanline++)
{
if (source->is_interlaced)
{
if (source->first_interlace)
{
register lcuint8 *sptr;
register lcuint32 col;
lcuint32 row;
for (row = 0; row < source->height; row++)
{
sptr = &source->interlaced_image[row*source->width];
for (col = source->width; col > 0; col--)
*sptr++ = (lcuint8) 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 lcuint8 *sptr, *ptr;
register lcuint32 col;
lcuint32 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 = *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 lcuint8 *ptr;
register lcuint32 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 true;
}
// =============================================================================
#undef LZW_TABLE_SIZE
#define MAX_LZW_BITS 12
typedef lcint16 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 lcint32 hash_entry;
#define HASH_ENTRY(prefix,suffix) ((((hash_entry) (prefix)) << 8) | (suffix))
typedef struct
{
int n_bits;
code_int maxcode;
int init_bits;
lcint32 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 *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 |= ((lcint32) 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;
}
bool Image::SaveGIF (File& file, bool transparent, bool interlaced, unsigned char* background) const
{
int InitCodeSize, FlagByte, i;
unsigned char pal[3][256];
unsigned char* colormappedbuffer = (unsigned char*)malloc (m_nWidth*m_nHeight);
dl1quant (m_pData, colormappedbuffer, m_nWidth, m_nHeight, 256, true, pal);
gif_dest_ptr dinfo;
dinfo = (gif_dest_ptr) malloc (sizeof(gif_dest_struct));
dinfo->output_file = &file;
dinfo->buffer = malloc(m_nWidth*sizeof(lcuint32));
dinfo->hash_code = (code_int*) malloc(HSIZE * sizeof(code_int));
dinfo->hash_value = (hash_entry*)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)m_nWidth);
put_word(file, (unsigned int)m_nHeight);
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)m_nWidth);
put_word(file, (unsigned int)m_nHeight);
// 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 < m_nHeight)
{
memcpy(dinfo->buffer, colormappedbuffer+(scanline*m_nWidth), m_nWidth);
register lcuint8 *ptr;
register lcuint32 col;
ptr = (unsigned char*)dinfo->buffer;
for (col = m_nWidth; col > 0; col--)
compress_byte(dinfo, *ptr++);
if (interlaced)
{
switch (pass)
{
case 0:
{
scanline += 8;
if (scanline >= m_nHeight)
{
pass++;
scanline = 4;
}
} break;
case 1:
{
scanline += 8;
if (scanline >= m_nHeight)
{
pass++;
scanline = 2;
}
} break;
case 2:
{
scanline += 4;
if (scanline >= m_nHeight)
{
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;
}

401
common/im_jpg.cpp Normal file
View file

@ -0,0 +1,401 @@
#include <setjmp.h>
#include <stdlib.h>
#include "config.h"
#include "image.h"
#include "file.h"
#ifdef LC_HAVE_JPEGLIB
extern "C" {
#include <jpeglib.h>
}
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;
}
}
// =============================================================================
// JPEG data source
// Expanded data source object for input using the File class
typedef struct
{
struct jpeg_source_mgr pub; // public fields
File * infile; // source stream
JOCTET * buffer; // start of buffer
boolean start_of_file; // have we gotten any data yet?
} my_source_mgr;
typedef my_source_mgr * my_src_ptr;
#define INPUT_BUF_SIZE 4096 // choose an efficiently fread'able size
// Initialize source --- called by jpeg_read_header
// before any data is actually read.
static void init_source (j_decompress_ptr cinfo)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
// We reset the empty-input-file flag for each image,
// but we don't clear the input buffer.
// This is correct behavior for reading a series of images from one source.
src->start_of_file = TRUE;
}
// Fill the input buffer --- called whenever buffer is emptied.
static boolean fill_input_buffer (j_decompress_ptr cinfo)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
size_t nbytes;
nbytes = src->infile->Read (src->buffer, INPUT_BUF_SIZE);
if (nbytes <= 0)
{
// if (src->start_of_file) // Treat empty input file as fatal error
// ERREXIT(cinfo, JERR_INPUT_EMPTY);
// WARNMS(cinfo, JWRN_JPEG_EOF);
// Insert a fake EOI marker
src->buffer[0] = (JOCTET) 0xFF;
src->buffer[1] = (JOCTET) JPEG_EOI;
nbytes = 2;
}
src->pub.next_input_byte = src->buffer;
src->pub.bytes_in_buffer = nbytes;
src->start_of_file = FALSE;
return TRUE;
}
// Skip data --- used to skip over a potentially large amount of
// uninteresting data (such as an APPn marker).
static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
// Just a dumb implementation for now. Could use Seek() except
// it doesn't work on pipes. Not clear that being smart is worth
// any trouble anyway --- large skips are infrequent.
if (num_bytes > 0)
{
while (num_bytes > (long) src->pub.bytes_in_buffer)
{
num_bytes -= (long) src->pub.bytes_in_buffer;
(void) fill_input_buffer(cinfo);
// note we assume that fill_input_buffer will never return FALSE,
// so suspension need not be handled.
}
src->pub.next_input_byte += (size_t) num_bytes;
src->pub.bytes_in_buffer -= (size_t) num_bytes;
}
}
// Terminate source --- called by jpeg_finish_decompress
// after all data has been read. Often a no-op.
static void term_source (j_decompress_ptr cinfo)
{
// no work necessary here
}
// Prepare for input from a File object.
static void jpeg_file_src (j_decompress_ptr cinfo, File& infile)
{
my_src_ptr src;
// The source object and input buffer are made permanent so that a series
// of JPEG images can be read from the same file by calling jpeg_stdio_src
// only before the first one. (If we discarded the buffer at the end of
// one image, we'd likely lose the start of the next one.)
// This makes it unsafe to use this manager and a different source
// manager serially with the same JPEG object. Caveat programmer.
if (cinfo->src == NULL)
{
// first time for this JPEG object?
cinfo->src = (struct jpeg_source_mgr *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
sizeof (my_source_mgr));
src = (my_src_ptr) cinfo->src;
src->buffer = (JOCTET *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
INPUT_BUF_SIZE * sizeof(JOCTET));
}
src = (my_src_ptr) cinfo->src;
src->pub.init_source = init_source;
src->pub.fill_input_buffer = fill_input_buffer;
src->pub.skip_input_data = skip_input_data;
src->pub.resync_to_restart = jpeg_resync_to_restart; // use default method
src->pub.term_source = term_source;
src->infile = &infile;
src->pub.bytes_in_buffer = 0; // forces fill_input_buffer on first read
src->pub.next_input_byte = NULL; // until buffer loaded
}
// =============================================================================
bool Image::LoadJPG (File& file)
{
struct jpeg_decompress_struct cinfo;
struct bt_jpeg_error_mgr jerr;
JSAMPARRAY buffer; // Output row buffer
int row_stride; // physical row width in output buffer
FreeData ();
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = bt_jpeg_error_exit;
if (setjmp(jerr.setjmp_buffer))
{
jpeg_destroy_decompress(&cinfo);
return false;
}
jpeg_create_decompress(&cinfo);
jpeg_file_src(&cinfo, file);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
// get our buffer set to hold data
m_pData = (unsigned char*)malloc(cinfo.output_width*cinfo.output_height*3);
if (m_pData == NULL)
{
// MessageBox(NULL, "Error", "Cannot allocate memory", MB_ICONSTOP);
jpeg_destroy_decompress(&cinfo);
return false;
}
m_nWidth = cinfo.output_width;
m_nHeight = cinfo.output_height;
m_bAlpha = false;
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], m_nWidth, m_pData, cinfo.output_scanline-1);
else if (cinfo.out_color_components == 1)
j_putGrayScanlineToRGB(buffer[0], m_nWidth, m_pData, cinfo.output_scanline-1);
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return true;
}
// =============================================================================
// JPEG data destination
// Expanded data destination object for output using the File class
typedef struct
{
struct jpeg_destination_mgr pub; // public fields
File * outfile; // target stream
JOCTET * buffer; // start of buffer
} my_destination_mgr;
typedef my_destination_mgr * my_dest_ptr;
#define OUTPUT_BUF_SIZE 4096 // choose an efficiently fwrite'able size
// Initialize destination --- called by jpeg_start_compress
// before any data is actually written.
static void init_destination (j_compress_ptr cinfo)
{
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
/* Allocate the output buffer --- it will be released when done with image */
dest->buffer = (JOCTET *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
OUTPUT_BUF_SIZE * sizeof(JOCTET));
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
}
// Empty the output buffer --- called whenever buffer fills up.
//
// In typical applications, this should write the entire output buffer
// (ignoring the current state of next_output_byte & free_in_buffer),
// reset the pointer & count to the start of the buffer, and return TRUE
// indicating that the buffer has been dumped.
static boolean empty_output_buffer (j_compress_ptr cinfo)
{
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
dest->outfile->Write (dest->buffer, OUTPUT_BUF_SIZE);
// if (dest->outfile.Write (dest->buffer, OUTPUT_BUF_SIZE) != (size_t) OUTPUT_BUF_SIZE)
// ERREXIT(cinfo, JERR_FILE_WRITE);
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
return TRUE;
}
// Terminate destination --- called by jpeg_finish_compress
// after all data has been written. Usually needs to flush buffer.
static void term_destination (j_compress_ptr cinfo)
{
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
// Write any data remaining in the buffer
if (datacount > 0)
{
dest->outfile->Write (dest->buffer, datacount);
// if (dest->outfile.Write (dest->buffer, datacount) != datacount)
// ERREXIT(cinfo, JERR_FILE_WRITE);
}
dest->outfile->Flush ();
// Make sure we wrote the output file OK
// if (ferror(dest->outfile))
// ERREXIT(cinfo, JERR_FILE_WRITE);
}
// Prepare for output to a File object.
static void jpeg_file_dest (j_compress_ptr cinfo, File& outfile)
{
my_dest_ptr dest;
// The destination object is made permanent so that multiple JPEG images
// can be written to the same file without re-executing jpeg_stdio_dest.
// This makes it dangerous to use this manager and a different destination
// manager serially with the same JPEG object, because their private object
// sizes may be different. Caveat programmer.
if (cinfo->dest == NULL)
{
// first time for this JPEG object?
cinfo->dest = (struct jpeg_destination_mgr *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
sizeof (my_destination_mgr));
}
dest = (my_dest_ptr) cinfo->dest;
dest->pub.init_destination = init_destination;
dest->pub.empty_output_buffer = empty_output_buffer;
dest->pub.term_destination = term_destination;
dest->outfile = &outfile;
}
// =============================================================================
bool Image::SaveJPG (File& file, int quality, bool progressive) const
{
struct jpeg_compress_struct cinfo;
struct bt_jpeg_error_mgr jerr;
int row_stride; // physical row widthPix in image buffer
// 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);
jpeg_file_dest(&cinfo, file);
cinfo.image_width = m_nWidth;
cinfo.image_height = m_nHeight;
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 = m_nWidth * 3;
while (cinfo.next_scanline < cinfo.image_height)
{
unsigned char* outRow = m_pData + (cinfo.next_scanline * m_nWidth * 3);
jpeg_write_scanlines(&cinfo, &outRow, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
return true;
}
#endif // LC_HAVE_JPEGLIB

375
common/im_png.cpp Executable file
View file

@ -0,0 +1,375 @@
#include <stdlib.h>
#include "config.h"
#include "image.h"
#include "file.h"
#ifdef LC_HAVE_PNGLIB
#include <png.h>
#define alpha_composite(composite, fg, alpha, bg) { \
unsigned short temp = ((unsigned short)(fg)*(unsigned short)(alpha) + \
(unsigned short)(bg)*(unsigned short)(255 - (unsigned short)(alpha)) + (unsigned short)128); \
(composite) = (unsigned char)((temp + (temp >> 8)) >> 8); \
}
// =============================================================================
static void user_read_fn (png_structp png_ptr, png_bytep data, png_size_t length)
{
png_size_t check;
// Read() returns 0 on error, so it is OK to store this in a png_size_t
// instead of an int, which is what Read() actually returns.
check = (png_size_t)((File*)png_ptr->io_ptr)->Read (data, length);
if (check != length)
png_error(png_ptr, "Read Error");
}
bool Image::LoadPNG (File& file)
{
unsigned char sig[8], red, green, blue;
unsigned char *image_data = NULL;
unsigned char *src, *dest;
unsigned char r, g, b, a;
unsigned long i, row;
unsigned long image_rowbytes;
png_color_16p pBackground;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
png_uint_32 width, height;
png_bytepp row_pointers = NULL;
int bit_depth, color_type;
int image_channels;
double gamma;
FreeData ();
file.Read (sig, 8);
if (!png_check_sig(sig, 8))
return false; // bad signature
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
return false; // out of memory
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
png_destroy_read_struct(&png_ptr, NULL, NULL);
return false; // out of memory
}
if (setjmp(png_ptr->jmpbuf))
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return false;
}
png_set_read_fn(png_ptr, (void *)&file, user_read_fn);
// png_init_io(png_ptr, f);
png_set_sig_bytes(png_ptr, 8); // we already read the 8 signature bytes
png_read_info(png_ptr, info_ptr); // read all PNG info up to image data
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
NULL, NULL, NULL);
if (setjmp(png_ptr->jmpbuf))
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return false;
}
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
{
png_get_bKGD(png_ptr, info_ptr, &pBackground);
if (setjmp (png_ptr->jmpbuf))
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return false;
}
// however, it always returns the raw bKGD data, regardless of any
// bit-depth transformations, so check depth and adjust if necessary
if (bit_depth == 16)
{
red = pBackground->red >> 8;
green = pBackground->green >> 8;
blue = pBackground->blue >> 8;
}
else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
{
if (bit_depth == 1)
red = green = blue = pBackground->gray? 255 : 0;
else if (bit_depth == 2)
red = green = blue = (255/3) * pBackground->gray;
else // bit_depth == 4
red = green = blue = (255/15) * pBackground->gray;
}
else
{
red = (unsigned char)pBackground->red;
green = (unsigned char)pBackground->green;
blue = (unsigned char)pBackground->blue;
}
}
else
{
if (setjmp (png_ptr->jmpbuf))
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return false;
}
red = green = blue = 0;
}
// expand palette images to RGB, low-bit-depth grayscale images to 8 bits,
// transparency chunks to full alpha channel; strip 16-bit-per-sample
// images to 8 bits per sample; and convert grayscale to RGB[A]
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_expand(png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand(png_ptr);
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_expand(png_ptr);
if (bit_depth == 16)
png_set_strip_16(png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
if (png_get_gAMA(png_ptr, info_ptr, &gamma))
png_set_gamma(png_ptr, 2.2, gamma);
// all transformations have been registered; now update info_ptr data,
// get rowbytes and channels, and allocate image memory
png_read_update_info(png_ptr, info_ptr);
image_rowbytes = png_get_rowbytes(png_ptr, info_ptr);
image_channels = (int)png_get_channels(png_ptr, info_ptr);
if ((image_data = (unsigned char*)malloc(image_rowbytes*height)) == NULL)
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return false;
}
if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL)
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
free(image_data);
return false;
}
// set the individual row_pointers to point at the correct offsets
for (i = 0; i < height; ++i)
row_pointers[i] = image_data + i*image_rowbytes;
// now we can go ahead and just read the whole image
png_read_image(png_ptr, row_pointers);
// and we're done! (png_read_end() can be omitted if no processing of
// post-IDAT text/time/etc. is desired)
free(row_pointers);
row_pointers = NULL;
png_read_end(png_ptr, NULL);
// done with PNG file, so clean up to minimize memory usage
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
if (!image_data)
return false;
// get our buffer set to hold data
m_pData = (unsigned char*)malloc(width*height*image_channels);
if (m_pData == NULL)
{
free (image_data);
return false;
}
m_nWidth = width;
m_nHeight = height;
if (image_channels == 3)
m_bAlpha = false;
else
m_bAlpha = true;
for (row = 0; row < height; row++)
{
src = image_data + row*image_rowbytes;
dest = m_pData + row*image_channels*width;
if (image_channels == 3)
{
for (i = width; i > 0; i--)
{
r = *src++;
g = *src++;
b = *src++;
*dest++ = r;
*dest++ = g;
*dest++ = b;
}
}
else // if (image_channels == 4)
{
for (i = width; i > 0; i--)
{
r = *src++;
g = *src++;
b = *src++;
a = *src++;
if (a == 255)
{
*dest++ = r;
*dest++ = g;
*dest++ = b;
}
else if (a == 0)
{
*dest++ = red;
*dest++ = green;
*dest++ = blue;
}
else
{
// this macro (copied from png.h) composites the
// foreground and background values and puts the
// result into the first argument; there are no
// side effects with the first argument
alpha_composite(*dest++, r, a, red);
alpha_composite(*dest++, g, a, green);
alpha_composite(*dest++, b, a, blue);
}
*dest++ = a;
}
}
}
free(image_data);
return true;
}
// =============================================================================
static void user_write_fn (png_structp png_ptr, png_bytep data, png_size_t length)
{
png_uint_32 check;
check = ((File*)png_ptr->io_ptr)->Write (data, length);
if (check != length)
{
png_error(png_ptr, "Write Error");
}
}
static void user_flush_fn (png_structp png_ptr)
{
((File*)png_ptr->io_ptr)->Flush ();
}
bool Image::SavePNG (File& file, bool transparent, bool interlaced, unsigned char* background) const
{
png_structp png_ptr;
png_infop info_ptr;
png_bytepp row_pointers = NULL;
png_color_8 sig_bit;
png_color_16 bg;
int i;
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
return false;
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
png_destroy_write_struct(&png_ptr, NULL);
return false;
}
if (setjmp(png_ptr->jmpbuf))
{
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
return false;
}
// png_init_io(png_ptr, fp);
png_set_write_fn (png_ptr, &file, user_write_fn, user_flush_fn);
png_set_IHDR (png_ptr, info_ptr, m_nWidth, m_nHeight, 8,
transparent ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB,
interlaced ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
bg.red = background[0];
bg.green = background[1];
bg.blue = background[2];
png_set_bKGD(png_ptr, info_ptr, &bg);
png_write_info(png_ptr, info_ptr);
// Set the true bit depth of the image data
sig_bit.red = 8;
sig_bit.green = 8;
sig_bit.blue = 8;
sig_bit.alpha = 8;
png_set_sBIT(png_ptr, info_ptr, &sig_bit);
if ((row_pointers = (png_bytepp)malloc(m_nHeight*sizeof(png_bytep))) == NULL)
{
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
return false;
}
// set the individual row_pointers to point at the correct offsets
if (transparent)
{
unsigned char *buf, *src, *dst, alpha;
dst = buf = (unsigned char*)malloc (m_nWidth*m_nHeight*4);
src = m_pData;
for (i = 0; i < m_nWidth*m_nHeight; i++)
{
if ((src[0] == background[0]) &&
(src[1] == background[1]) &&
(src[2] == background[2]))
alpha = 0;
else
alpha = 255;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = alpha;
}
for (i = 0; i < m_nHeight; i++)
row_pointers[i] = buf + i*m_nWidth*4;
png_write_image(png_ptr, row_pointers);
free(buf);
}
else
{
for (i = 0; i < m_nHeight; i++)
row_pointers[i] = m_pData + i*m_nWidth*3;
png_write_image(png_ptr, row_pointers);
}
free(row_pointers);
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
return true;
}
#endif // LC_HAVE_PNGLIB

512
common/image.cpp Normal file
View file

@ -0,0 +1,512 @@
// Image I/O routines
//
#include "opengl.h"
#ifdef LC_WINDOWS
#include <windows.h>
#include <windowsx.h>
//#include <mmsystem.h>
#include <vfw.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "config.h"
#include "image.h"
#include "file.h"
// =============================================================================
// Image functions
Image::Image ()
{
m_nWidth = 0;
m_nHeight = 0;
m_bAlpha = false;
m_pData = NULL;
}
Image::~Image ()
{
free (m_pData);
}
void Image::FreeData ()
{
m_nWidth = 0;
m_nHeight = 0;
m_bAlpha = false;
free (m_pData);
m_pData = NULL;
}
void Image::Allocate (int width, int height, bool alpha)
{
FreeData ();
m_nWidth = width;
m_nHeight = height;
m_bAlpha = alpha;
if (m_bAlpha)
m_pData = (unsigned char*)malloc (width * height * 4);
else
m_pData = (unsigned char*)malloc (width * height * 3);
}
void Image::ResizePow2 ()
{
int i, shifted_x, shifted_y;
shifted_x = m_nWidth;
for (i = 0; ((i < 16) && (shifted_x != 0)); i++)
shifted_x = shifted_x >> 1;
shifted_x = (i != 0) ? 1 << (i-1) : 1;
shifted_y = m_nHeight;
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 != m_nWidth) || (shifted_y != m_nHeight))
Resize (shifted_x, shifted_y);
}
void Image::Resize (int width, int height)
{
int i, j, k, components, stx, sty;
float accumx, accumy;
unsigned char* bits;
if (m_bAlpha)
components = 4;
else
components = 3;
bits = (unsigned char*)malloc (width * height * components);
for (j = 0; j < m_nHeight; j++)
{
accumy = (float)height*j/(float)m_nHeight;
sty = (int)floor(accumy);
for (i = 0; i < m_nWidth; i++)
{
accumx = (float)width*i/(float)m_nWidth;
stx = (int)floor(accumx);
for (k = 0; k < components; k++)
bits[(stx+sty*width)*components+k] = m_pData[(i+j*m_nWidth)*components+k];
}
}
free (m_pData);
m_pData = bits;
m_nWidth = width;
m_nHeight = height;
}
void Image::FromOpenGL (int width, int height)
{
unsigned char *buf;
buf = (unsigned char*)malloc (width*height*3);
FreeData ();
m_pData = (unsigned char*)malloc (width*height*3);
m_nWidth = width;
m_nHeight = height;
m_bAlpha = false;
glPixelStorei (GL_PACK_ALIGNMENT, 1);
glReadPixels (0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, buf);
for (int row = 0; row < height; row++)
memcpy (m_pData + (row*width*3), buf + ((height-row-1)*width*3), width*3);
free (buf);
}
bool Image::FileLoad (File& file)
{
unsigned char buf[8];
// Read a few bytes
if (file.Read (buf, 8) != 8)
return false;
file.Seek (-8, SEEK_CUR);
// Check for the BMP header
if ((buf[0] == 'B') && (buf[1] == 'M'))
{
if (!LoadBMP (file))
return false;
return true;
}
#ifdef LC_HAVE_JPEGLIB
if ((buf[0] == 0xFF) && (buf[1] == 0xD8))
{
if (!LoadJPG (file))
return false;
return true;
}
#endif
#ifdef LC_HAVE_PNGLIB
const unsigned char png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
// Check for the PNG header
if (memcmp (buf, png_signature, 8) == 0)
{
if (!LoadPNG (file))
return false;
return true;
}
#endif
// Check for the GIF header
if ((buf[0] == 'G') && (buf[1] == 'I') && (buf[2] == 'F') &&
(buf[3] == '8') && ((buf[4] == '7') || (buf[4] == '9')) &&
(buf[5] == 'a'))
{
if (!LoadGIF (file))
return false;
return true;
}
// MessageBox (NULL, "Unknown File Format", "Error", MB_ICONSTOP);
return false;
}
bool Image::FileLoad (const char* filename)
{
FileDisk file;
if (!file.Open (filename, "rb"))
return false;
return FileLoad (file);
}
bool Image::FileSave (File& file, LC_IMAGE_OPTS* opts) const
{
switch (opts->format)
{
#ifdef LC_HAVE_JPEGLIB
case LC_IMAGE_JPG:
return SaveJPG (file, opts->quality, opts->interlaced);
#endif
case LC_IMAGE_GIF:
return SaveGIF (file, opts->transparent, opts->interlaced, opts->background);
case LC_IMAGE_BMP:
return SaveBMP (file, opts->truecolor == false);
#ifdef LC_HAVE_PNGLIB
case LC_IMAGE_PNG:
return SavePNG (file, opts->transparent, opts->interlaced, opts->background);
#endif
default:
break;
}
// MessageBox (NULL, "Could not save file", "Error", MB_ICONSTOP);
return false;
}
bool Image::FileSave (const char* filename, LC_IMAGE_OPTS* opts) const
{
char name[LC_MAXPATH], ext[5], *p;
FileDisk file;
bool needext = false;
strcpy (name, filename);
p = name + strlen (name) - 1;
while ((p > name) && (*p != '/') && (*p != '\\') && (*p != '.'))
p--;
if (*p != '.')
needext = true;
else
{
if (strlen (p) > 5)
needext = true;
else
{
strcpy (ext, p+1);
strlwr (ext);
if (strcmp (ext, "bmp") == 0)
opts->format = LC_IMAGE_BMP;
else if (strcmp (ext, "gif") == 0)
opts->format = LC_IMAGE_GIF;
#ifdef LC_HAVE_JPEGLIB
else if (strcmp (ext, "jpg") == 0)
opts->format = LC_IMAGE_JPG;
else if (strcmp (ext, "jpeg") == 0)
opts->format = LC_IMAGE_JPG;
#endif
#ifdef LC_HAVE_PNGLIB
else if (strcmp (ext, "png") == 0)
opts->format = LC_IMAGE_PNG;
#endif
else
needext = true;
}
}
if (needext)
{
// no extension, add from the options
switch (opts->format)
{
case LC_IMAGE_BMP:
strcat (name, ".bmp");
break;
case LC_IMAGE_GIF:
strcat (name, ".gif");
break;
#ifdef LC_HAVE_JPEGLIB
case LC_IMAGE_JPG:
strcat (name, ".jpg");
break;
#endif
#ifdef LC_HAVE_PNGLIB
case LC_IMAGE_PNG:
strcat (name, ".png");
break;
#endif
default:
return false;
}
}
if (!file.Open (name, "wb"))
return false;
return FileSave (file, opts);
}
// =============================================================================
// Global functions
#ifdef LC_WINDOWS
#include "system.h"
#define AVIIF_KEYFRAME 0x00000010L // this frame is a key frame.
#define LPLPBI LPBITMAPINFOHEADER *
static HANDLE MakeDib (HBITMAP hbitmap, Image& image)
{
HANDLE hdib ;
HDC hdc ;
BITMAP bitmap ;
UINT wLineLen ;
DWORD dwSize ;
DWORD wColSize ;
LPBITMAPINFOHEADER lpbi ;
LPBYTE lpBits ;
UINT bits = 24;
int i, j;
GetObject(hbitmap,sizeof(BITMAP),&bitmap) ;
// DWORD align the width of the DIB
// Figure out the size of the colour table
// Calculate the size of the DIB
wLineLen = (bitmap.bmWidth*bits+31)/32 * 4;
wColSize = sizeof(RGBQUAD)*((bits <= 8) ? 1<<bits : 0);
dwSize = sizeof(BITMAPINFOHEADER) + wColSize +
(DWORD)(UINT)wLineLen*(DWORD)(UINT)bitmap.bmHeight;
// Allocate room for a DIB and set the LPBI fields
hdib = GlobalAlloc(GHND,dwSize);
if (!hdib)
return hdib ;
lpbi = (LPBITMAPINFOHEADER)GlobalLock(hdib) ;
lpbi->biSize = sizeof(BITMAPINFOHEADER) ;
lpbi->biWidth = bitmap.bmWidth ;
lpbi->biHeight = bitmap.bmHeight ;
lpbi->biPlanes = 1 ;
lpbi->biBitCount = (WORD) bits ;
lpbi->biCompression = BI_RGB ;
lpbi->biSizeImage = dwSize - sizeof(BITMAPINFOHEADER) - wColSize ;
lpbi->biXPelsPerMeter = 0 ;
lpbi->biYPelsPerMeter = 0 ;
lpbi->biClrUsed = (bits <= 8) ? 1<<bits : 0;
lpbi->biClrImportant = 0 ;
// Get the bits from the bitmap and stuff them after the LPBI
lpBits = (LPBYTE)(lpbi+1)+wColSize ;
hdc = CreateCompatibleDC(NULL) ;
GetDIBits(hdc,hbitmap,0,bitmap.bmHeight,lpBits,(LPBITMAPINFO)lpbi, DIB_RGB_COLORS);
for (i = 0; i < lpbi->biHeight; i++)
{
unsigned char *src = image.GetData() + i * image.Width() * 3;
unsigned char *dst = lpBits + (lpbi->biHeight - i - 1) * wLineLen;
for (j = 0; j < lpbi->biWidth; j++)
{
dst[0] = src[2];
dst[1] = src[1];
dst[2] = src[0];
src += 3;
dst += 3;
}
}
// Fix this if GetDIBits messed it up....
lpbi->biClrUsed = (bits <= 8) ? 1<<bits : 0;
DeleteDC(hdc) ;
GlobalUnlock(hdib);
return hdib ;
}
// Walk through our array of LPBI's and free them
static void FreeFrames(LPLPBI alpbi, int frames)
{
int w;
if (!alpbi[0])
return;
// Don't free a frame if it's a duplicate of the previous one
for (w=0; w < frames; w++)
if (alpbi[w] && alpbi[w] != alpbi[w-1])
GlobalFreePtr(alpbi[w]);
for (w=0; w < frames; w++)
alpbi[w] = NULL;
}
// Fill an array of LPBI's with the frames for this movie
static void MakeFrames (LPLPBI alpbi, Image *images, int frames)
{
HBITMAP hbitmap, hbitmapOld;
HDC hdc, hdcMem;
int i;
RECT rc;
hdc = GetDC (NULL);
hdcMem = CreateCompatibleDC (NULL);
hbitmap = CreateCompatibleBitmap (hdc, images[0].Width (), images[0].Height ());
// Now walk through and make all the frames
for ( i=0; i < frames; i++ )
{
hbitmapOld = SelectBitmap(hdcMem, hbitmap);
// Fill the whole frame with white
SetRect(&rc,0,0,images[0].Width (), images[0].Height ());
FillRect(hdcMem,&rc,GetStockBrush(WHITE_BRUSH));
SelectBitmap(hdcMem, hbitmapOld);
// Make this into a DIB and stuff it into the array
alpbi[i] = (LPBITMAPINFOHEADER)GlobalLock(MakeDib(hbitmap, images[i]));
// For an error, just duplicate the last frame if we can
if (alpbi[i] == NULL && i )
alpbi[i] = alpbi[i-1] ;
}
// Select all the old objects back and delete resources
DeleteBitmap(hbitmap) ;
DeleteObject(hdcMem) ;
ReleaseDC(NULL,hdc) ;
}
void SaveVideo(char* filename, Image *images, int count, float fps)
{
AVISTREAMINFO strhdr;
PAVIFILE pfile = NULL;
PAVISTREAM ps = NULL, psCompressed = NULL;
AVICOMPRESSOPTIONS opts;
AVICOMPRESSOPTIONS FAR * aopts[1] = { &opts };
LPBITMAPINFOHEADER *plpbi;
_fmemset(&opts, 0, sizeof(opts));
// first let's make sure we are running on 1.1
WORD wVer = HIWORD(VideoForWindowsVersion());
if (wVer < 0x010a)
{
SystemDoMessageBox("Video for Windows 1.1 or later required", MB_OK|MB_ICONSTOP);
return;
}
AVIFileInit();
plpbi = (LPBITMAPINFOHEADER*) malloc (count*sizeof (LPBITMAPINFOHEADER));
MakeFrames (plpbi, images, count);
if (AVIFileOpen(&pfile, filename, 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(NULL, 0, 1, &ps, (LPAVICOMPRESSOPTIONS FAR *) &aopts))
// 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)Sys_ProfileLoadInt("Default", "AVI Pause", 100)/100;
int time = (int)(fPause * 15);
///////////// set FPS
time = 1;
for (int i = 0; i < count; 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;
}
}
}
FreeFrames (plpbi, count);
// Now close the file
if (ps) AVIStreamClose(ps);
if (psCompressed) AVIStreamClose(psCompressed);
if (pfile) AVIFileClose(pfile);
AVIFileExit();
}
#else
void SaveVideo(char* filename, Image *images, int count, float fps)
{
// SystemDoMessageBox("Format not supported under this platform.", LC_MB_OK|LC_MB_ERROR);
}
#endif

54
common/image.h Normal file
View file

@ -0,0 +1,54 @@
#ifndef _IMAGE_H_
#define _IMAGE_H_
class File;
#include "typedefs.h"
class Image
{
public:
Image ();
virtual ~Image ();
bool FileSave (File& file, LC_IMAGE_OPTS* opts) const;
bool FileSave (const char* filename, LC_IMAGE_OPTS* opts) const;
bool FileLoad (File& file);
bool FileLoad (const char* filename);
void Resize (int width, int height);
void ResizePow2 ();
void FromOpenGL (int width, int height);
void Allocate (int width, int height, bool alpha);
int Width () const
{ return m_nWidth; }
int Height () const
{ return m_nHeight; }
int Alpha () const
{ return m_bAlpha; }
unsigned char* GetData () const
{ return m_pData; }
protected:
void FreeData ();
bool LoadJPG (File& file);
bool LoadBMP (File& file);
bool LoadPNG (File& file);
bool LoadGIF (File& file);
bool SaveJPG (File& file, int quality, bool progressive) const;
bool SaveBMP (File& file, bool quantize) const;
bool SavePNG (File& file, bool transparent, bool interlaced, unsigned char* background) const;
bool SaveGIF (File& file, bool transparent, bool interlaced, unsigned char* background) const;
int m_nWidth;
int m_nHeight;
bool m_bAlpha;
unsigned char* m_pData;
};
void SaveVideo(char* filename, Image *images, int count, float fps);
#endif // _IMAGE_H_

433
common/keyboard.cpp Normal file
View file

@ -0,0 +1,433 @@
//
// Code to handle user-defined keyboard shortcuts.
//
#include <stdio.h>
#include "system.h"
#include "keyboard.h"
#include "file.h"
#include "str.h"
// ============================================================================
// Globals.
LC_KEYBOARD_COMMAND DefaultKeyboardShortcuts[] =
{
{ LC_FILE_NEW, "New Project", LC_KEYMOD1_CONTROL, LC_KEY_N, 0 },
{ LC_FILE_OPEN, "Open Project", LC_KEYMOD1_CONTROL, LC_KEY_O, 0 },
{ LC_FILE_MERGE, "Merge Project", 0, 0, 0 },
{ LC_FILE_SAVE, "Save Project", LC_KEYMOD1_CONTROL, LC_KEY_S, 0 },
{ LC_FILE_SAVEAS, "Save Project As", 0, 0, 0 },
{ LC_FILE_PICTURE, "Save Picture", 0, 0, 0 },
{ LC_FILE_3DS, "Export 3D Studio", 0, 0, 0 },
{ LC_FILE_HTML, "Export HTML", 0, 0, 0 },
{ LC_FILE_POVRAY, "Export POV-Ray", 0, 0, 0 },
{ LC_FILE_WAVEFRONT, "Export Wavefront", 0, 0, 0 },
{ LC_FILE_PROPERTIES, "Project Properties", 0, 0, 0 },
// { LC_FILE_TERRAIN, "Terrain Editor", 0, 0, 0 },
{ LC_FILE_LIBRARY, "Piece Library Manager", 0, 0, 0 },
// { LC_FILE_RECENT, "Open Recent File", 0, 0, 0 },
{ LC_EDIT_UNDO, "Undo", LC_KEYMOD1_CONTROL, LC_KEY_Z, 0 },
{ LC_EDIT_REDO, "Redo", LC_KEYMOD1_CONTROL, LC_KEY_Y, 0 },
{ LC_EDIT_CUT, "Cut", LC_KEYMOD1_CONTROL, LC_KEY_X, 0 },
{ LC_EDIT_COPY, "Copy", LC_KEYMOD1_CONTROL, LC_KEY_C, 0 },
{ LC_EDIT_PASTE, "Paste", LC_KEYMOD1_CONTROL, LC_KEY_V, 0 },
{ LC_EDIT_SELECT_ALL, "Select All", LC_KEYMOD1_CONTROL, LC_KEY_A, 0 },
{ LC_EDIT_SELECT_NONE, "Select None", 0, 0, 0 },
{ LC_EDIT_SELECT_INVERT, "Select Invert", 0, 0, 0 },
{ LC_EDIT_SELECT_BYNAME, "Select By Name", 0, 0, 0 },
{ LC_PIECE_INSERT, "Piece Insert", 0, LC_KEY_INSERT, 0 },
{ LC_PIECE_DELETE, "Piece Delete", 0, LC_KEY_DELETE, 0 },
// { LC_PIECE_MINIFIG, "Minifig Wizard", 0, 0, 0 },
{ LC_PIECE_ARRAY, "Piece Array", 0, 0, 0 },
// { LC_PIECE_COPYKEYS, "", 0, 0, 0 },
{ LC_PIECE_GROUP, "Piece Group", LC_KEYMOD1_CONTROL, LC_KEY_G, 0 },
{ LC_PIECE_UNGROUP, "Piece Ungroup", LC_KEYMOD1_CONTROL, LC_KEY_U, 0 },
{ LC_PIECE_GROUP_ADD, "Group Add Piece", 0, 0, 0 },
{ LC_PIECE_GROUP_REMOVE, "Group Remove Piece", 0, 0, 0 },
{ LC_PIECE_GROUP_EDIT, "Group Edit", 0, 0, 0 },
{ LC_PIECE_HIDE_SELECTED, "Hide Selection", LC_KEYMOD1_CONTROL, LC_KEY_H, 0 },
{ LC_PIECE_HIDE_UNSELECTED, "Unhide Selection", 0, 0, 0 },
{ LC_PIECE_UNHIDE_ALL, "Unhide All", 0, 0, 0 },
{ LC_PIECE_PREVIOUS, "Piece Previous Step", 0, 0, 0 },
{ LC_PIECE_NEXT, "Piece Next Step", 0, 0, 0 },
{ LC_VIEW_PREFERENCES, "Preferences", 0, 0, 0 },
// { LC_VIEW_ZOOM, "", 0, 0, 0 },
{ LC_VIEW_ZOOMIN, "Zoom In", 0, 0, 0 },
{ LC_VIEW_ZOOMOUT, "Zoom Out", 0, 0, 0 },
{ LC_VIEW_ZOOMEXTENTS, "Zoom Extents", 0, 0, 0 },
// { LC_VIEW_VIEWPORTS, "", 0, 0, 0 },
{ LC_VIEW_STEP_NEXT, "Step Next", 0, 0, 0 },
{ LC_VIEW_STEP_PREVIOUS, "Step Previous", 0, 0, 0 },
{ LC_VIEW_STEP_FIRST, "Step First", 0, 0, 0 },
{ LC_VIEW_STEP_LAST, "Step Last", 0, 0, 0 },
// { LC_VIEW_STEP_CHOOSE, "", 0, 0, 0 },
// { LC_VIEW_STEP_SET, "", 0, 0, 0 },
// { LC_VIEW_STOP, "", 0, 0, 0 },
// { LC_VIEW_PLAY, "", 0, 0, 0 },
{ LC_VIEW_CAMERA_FRONT, "Camera Front", LC_KEYMOD_VIEWONLY, LC_KEY_F, 0 },
{ LC_VIEW_CAMERA_BACK, "Camera Back", LC_KEYMOD_VIEWONLY, LC_KEY_B, 0 },
{ LC_VIEW_CAMERA_TOP, "Camera Top", LC_KEYMOD_VIEWONLY, LC_KEY_T, 0 },
{ LC_VIEW_CAMERA_BOTTOM, "Camera Bottom", LC_KEYMOD_VIEWONLY, LC_KEY_U, 0 },
{ LC_VIEW_CAMERA_LEFT, "Camera Left", LC_KEYMOD_VIEWONLY, LC_KEY_L, 0 },
{ LC_VIEW_CAMERA_RIGHT, "Camera Right", LC_KEYMOD_VIEWONLY, LC_KEY_R, 0 },
{ LC_VIEW_CAMERA_MAIN, "Camera Main", LC_KEYMOD_VIEWONLY, LC_KEY_M, 0 },
// { LC_VIEW_CAMERA_MENU, "", 0, 0, 0 },
// { LC_VIEW_CAMERA_RESET, "", 0, 0, 0 },
// { LC_VIEW_AUTOPAN, "", 0, 0, 0 },
// { LC_HELP_ABOUT, "", 0, 0, 0 },
// { LC_TOOLBAR_ANIMATION, "", 0, 0, 0 },
// { LC_TOOLBAR_ADDKEYS, "", 0, 0, 0 },
// { LC_TOOLBAR_SNAPMENU, "", 0, 0, 0 },
// { LC_TOOLBAR_LOCKMENU, "", 0, 0, 0 },
// { LC_TOOLBAR_FASTRENDER, "", 0, 0, 0 },
// { LC_TOOLBAR_BACKGROUND, "", 0, 0, 0 },
{ LC_VIEW_STEP_INSERT, "Step Insert", 0, 0, 0 },
{ LC_VIEW_STEP_DELETE, "Step Delete", 0, 0, 0 },
{ LC_EDIT_MOVEXY_SNAP_0, "Move XY Snap 0", 0, LC_KEY_0, 0 },
{ LC_EDIT_MOVEXY_SNAP_1, "Move XY Snap 1", 0, LC_KEY_1, 0 },
{ LC_EDIT_MOVEXY_SNAP_2, "Move XY Snap 2", 0, LC_KEY_2, 0 },
{ LC_EDIT_MOVEXY_SNAP_3, "Move XY Snap 3", 0, LC_KEY_3, 0 },
{ LC_EDIT_MOVEXY_SNAP_4, "Move XY Snap 4", 0, LC_KEY_4, 0 },
{ LC_EDIT_MOVEXY_SNAP_5, "Move XY Snap 5", 0, LC_KEY_5, 0 },
{ LC_EDIT_MOVEXY_SNAP_6, "Move XY Snap 6", 0, LC_KEY_6, 0 },
{ LC_EDIT_MOVEXY_SNAP_7, "Move XY Snap 7", 0, LC_KEY_7, 0 },
{ LC_EDIT_MOVEXY_SNAP_8, "Move XY Snap 8", 0, LC_KEY_8, 0 },
{ LC_EDIT_MOVEXY_SNAP_9, "Move XY Snap 9", 0, LC_KEY_9, 0 },
{ LC_EDIT_MOVEZ_SNAP_0, "Move Z Snap 0", LC_KEYMOD1_SHIFT|LC_KEYMOD1_CONTROL, LC_KEY_0, 0 },
{ LC_EDIT_MOVEZ_SNAP_1, "Move Z Snap 1", LC_KEYMOD1_SHIFT|LC_KEYMOD1_CONTROL, LC_KEY_1, 0 },
{ LC_EDIT_MOVEZ_SNAP_2, "Move Z Snap 2", LC_KEYMOD1_SHIFT|LC_KEYMOD1_CONTROL, LC_KEY_2, 0 },
{ LC_EDIT_MOVEZ_SNAP_3, "Move Z Snap 3", LC_KEYMOD1_SHIFT|LC_KEYMOD1_CONTROL, LC_KEY_3, 0 },
{ LC_EDIT_MOVEZ_SNAP_4, "Move Z Snap 4", LC_KEYMOD1_SHIFT|LC_KEYMOD1_CONTROL, LC_KEY_4, 0 },
{ LC_EDIT_MOVEZ_SNAP_5, "Move Z Snap 5", LC_KEYMOD1_SHIFT|LC_KEYMOD1_CONTROL, LC_KEY_5, 0 },
{ LC_EDIT_MOVEZ_SNAP_6, "Move Z Snap 6", LC_KEYMOD1_SHIFT|LC_KEYMOD1_CONTROL, LC_KEY_6, 0 },
{ LC_EDIT_MOVEZ_SNAP_7, "Move Z Snap 7", LC_KEYMOD1_SHIFT|LC_KEYMOD1_CONTROL, LC_KEY_7, 0 },
{ LC_EDIT_MOVEZ_SNAP_8, "Move Z Snap 8", LC_KEYMOD1_SHIFT|LC_KEYMOD1_CONTROL, LC_KEY_8, 0 },
{ LC_EDIT_MOVEZ_SNAP_9, "Move Z Snap 9", LC_KEYMOD1_SHIFT|LC_KEYMOD1_CONTROL, LC_KEY_9, 0 },
{ LC_EDIT_ANGLE_SNAP_0, "Angle Snap 1", LC_KEYMOD1_SHIFT, LC_KEY_0, 0 },
{ LC_EDIT_ANGLE_SNAP_1, "Angle Snap 5", LC_KEYMOD1_SHIFT, LC_KEY_1, 0 },
{ LC_EDIT_ANGLE_SNAP_2, "Angle Snap 10", LC_KEYMOD1_SHIFT, LC_KEY_2, 0 },
{ LC_EDIT_ANGLE_SNAP_3, "Angle Snap 15", LC_KEYMOD1_SHIFT, LC_KEY_3, 0 },
{ LC_EDIT_ANGLE_SNAP_4, "Angle Snap 30", LC_KEYMOD1_SHIFT, LC_KEY_4, 0 },
{ LC_EDIT_ANGLE_SNAP_5, "Angle Snap 45", LC_KEYMOD1_SHIFT, LC_KEY_5, 0 },
{ LC_EDIT_ANGLE_SNAP_6, "Angle Snap 60", LC_KEYMOD1_SHIFT, LC_KEY_6, 0 },
{ LC_EDIT_ANGLE_SNAP_7, "Angle Snap 90", LC_KEYMOD1_SHIFT, LC_KEY_7, 0 },
{ LC_EDIT_ANGLE_SNAP_8, "Angle Snap 180", LC_KEYMOD1_SHIFT, LC_KEY_8, 0 },
{ LC_EDIT_ACTION_SELECT, "Select Mode", 0, 0, 0 },
{ LC_EDIT_ACTION_INSERT, "Insert Mode", 0, 0, 0 },
{ LC_EDIT_ACTION_LIGHT, "Light Mode", 0, 0, 0 },
{ LC_EDIT_ACTION_SPOTLIGHT, "Spotlight Mode", 0, 0, 0 },
{ LC_EDIT_ACTION_CAMERA, "Camera Mode", 0, 0, 0 },
{ LC_EDIT_ACTION_MOVE, "Move Mode", LC_KEYMOD1_SHIFT, LC_KEY_M, 0 },
{ LC_EDIT_ACTION_ROTATE, "Rotate Mode", LC_KEYMOD1_SHIFT, LC_KEY_R, 0 },
{ LC_EDIT_ACTION_ERASER, "Eraser Mode", LC_KEYMOD1_SHIFT, LC_KEY_E, 0 },
{ LC_EDIT_ACTION_PAINT, "Paint Mode", LC_KEYMOD1_SHIFT, LC_KEY_N, 0 },
{ LC_EDIT_ACTION_ZOOM, "Zoom Mode", LC_KEYMOD1_SHIFT, LC_KEY_Z, 0 },
{ LC_EDIT_ACTION_ZOOM_REGION, "Zoom Region Mode", 0, 0, 0 },
{ LC_EDIT_ACTION_PAN, "Pan Mode", LC_KEYMOD1_SHIFT, LC_KEY_P, 0 },
{ LC_EDIT_ACTION_ROTATE_VIEW, "Rotate View Mode", LC_KEYMOD1_SHIFT, LC_KEY_T, 0 },
{ LC_EDIT_ACTION_ROLL, "Roll Camera Mode", LC_KEYMOD1_SHIFT, LC_KEY_L, 0 },
};
const int KeyboardShortcutsCount = sizeof(DefaultKeyboardShortcuts)/sizeof(KeyboardShortcuts[0]);
LC_KEYBOARD_COMMAND KeyboardShortcuts[KeyboardShortcutsCount];
// ============================================================================
// Functions
bool SaveKeyboardShortcuts(const char* FileName)
{
FileDisk f;
if (!f.Open(FileName, "wt"))
return false;
for (int i = 0; i < KeyboardShortcutsCount; i++)
{
LC_KEYBOARD_COMMAND& Cmd = KeyboardShortcuts[i];
String str;
str = Cmd.Description;
str += "=";
if (Cmd.Key1)
{
if (Cmd.Flags & LC_KEYMOD1_SHIFT)
str += "Shift+";
if (Cmd.Flags & LC_KEYMOD1_CONTROL)
str += "Ctrl+";
str += "\"";
str += GetKeyName(Cmd.Key1);
str += "\"";
}
if (Cmd.Key2)
{
str += ",";
if (Cmd.Flags & LC_KEYMOD2_SHIFT)
str += "Shift+";
if (Cmd.Flags & LC_KEYMOD2_CONTROL)
str += "Ctrl+";
str += "\"";
str += GetKeyName(Cmd.Key2);
str += "\"";
}
str += "\n";
f.Write((const char*)str, str.GetLength());
}
return true;
}
bool LoadKeyboardShortcuts(const char* FileName)
{
FileDisk f;
int i;
if (!f.Open(FileName, "rt"))
return false;
// Remove all existing shortcuts
for (i = 0; i < KeyboardShortcutsCount; i++)
{
LC_KEYBOARD_COMMAND& Cmd = KeyboardShortcuts[i];
Cmd.Key1 = 0;
Cmd.Key2 = 0;
Cmd.Flags = DefaultKeyboardShortcuts[i].Flags & ~LC_KEYMOD_MASK;
}
char Line[1024];
while (f.ReadLine(Line, 1024))
{
char* ptr = strchr(Line, '=');
if (ptr == NULL)
continue;
*ptr = 0;
ptr++;
for (i = 0; i < KeyboardShortcutsCount; i++)
{
LC_KEYBOARD_COMMAND& Cmd = KeyboardShortcuts[i];
if (strcmp(Line, Cmd.Description))
continue;
if (!strncmp(ptr, "Shift+", 6))
{
Cmd.Flags |= LC_KEYMOD1_SHIFT;
ptr += 6;
}
if (!strncmp(ptr, "Ctrl+", 5))
{
Cmd.Flags |= LC_KEYMOD1_CONTROL;
ptr += 5;
}
ptr++;
char* ptr2 = strchr(ptr, '\"');
if (ptr2 == NULL)
{
Cmd.Flags &= ~(LC_KEYMOD1_SHIFT | LC_KEYMOD1_CONTROL);
break;
}
*ptr2 = 0;
Cmd.Key1 = GetKeyFromName(ptr);
ptr = ptr2 + 1;
if (*ptr != ',')
break;
ptr++;
if (!strncmp(ptr, "Shift+", 6))
{
Cmd.Flags |= LC_KEYMOD2_SHIFT;
ptr += 6;
}
if (!strncmp(ptr, "Ctrl+", 5))
{
Cmd.Flags |= LC_KEYMOD2_CONTROL;
ptr += 5;
}
ptr++;
ptr2 = strchr(ptr, '\"');
if (ptr2 == NULL)
{
Cmd.Flags &= ~(LC_KEYMOD2_SHIFT | LC_KEYMOD2_CONTROL);
break;
}
*ptr2 = 0;
Cmd.Key2 = GetKeyFromName(ptr);
break;
}
}
return true;
}
void ResetKeyboardShortcuts()
{
memcpy(KeyboardShortcuts, DefaultKeyboardShortcuts, sizeof(KeyboardShortcuts));
}
void InitKeyboardShortcuts()
{
const char* FileName = Sys_ProfileLoadString("Settings", "Keyboard", "");
ResetKeyboardShortcuts();
LoadKeyboardShortcuts(FileName);
}
typedef struct
{
int Key;
const char* Name;
} LC_KEYNAME_ENTRY;
static LC_KEYNAME_ENTRY KeyNames[] =
{
{ LC_KEY_BACK, "Backspace" },
{ LC_KEY_TAB, "Tab" },
{ LC_KEY_RETURN, "Return" },
{ LC_KEY_PAUSE, "Pause" },
{ LC_KEY_CAPITAL, "Caps" },
{ LC_KEY_ESCAPE, "Escape" },
{ LC_KEY_SPACE, "Space" },
{ LC_KEY_PRIOR, "Page Up" },
{ LC_KEY_NEXT, "Page Down" },
{ LC_KEY_END, "End" },
{ LC_KEY_HOME, "Home" },
{ LC_KEY_LEFT, "Left" },
{ LC_KEY_UP, "Up" },
{ LC_KEY_RIGHT, "Right" },
{ LC_KEY_DOWN, "Down" },
{ LC_KEY_SELECT, "Select" },
{ LC_KEY_PRINT, "Print" },
{ LC_KEY_INSERT, "Insert" },
{ LC_KEY_DELETE, "Delete" },
{ LC_KEY_0, "0" },
{ LC_KEY_1, "1" },
{ LC_KEY_2, "2" },
{ LC_KEY_3, "3" },
{ LC_KEY_4, "4" },
{ LC_KEY_5, "5" },
{ LC_KEY_6, "6" },
{ LC_KEY_7, "7" },
{ LC_KEY_8, "8" },
{ LC_KEY_9, "9" },
{ LC_KEY_A, "A" },
{ LC_KEY_B, "B" },
{ LC_KEY_C, "C" },
{ LC_KEY_D, "D" },
{ LC_KEY_E, "E" },
{ LC_KEY_F, "F" },
{ LC_KEY_G, "G" },
{ LC_KEY_H, "H" },
{ LC_KEY_I, "I" },
{ LC_KEY_J, "J" },
{ LC_KEY_K, "K" },
{ LC_KEY_L, "L" },
{ LC_KEY_M, "M" },
{ LC_KEY_N, "N" },
{ LC_KEY_O, "O" },
{ LC_KEY_P, "P" },
{ LC_KEY_Q, "Q" },
{ LC_KEY_R, "R" },
{ LC_KEY_S, "S" },
{ LC_KEY_T, "T" },
{ LC_KEY_U, "U" },
{ LC_KEY_V, "V" },
{ LC_KEY_W, "W" },
{ LC_KEY_X, "X" },
{ LC_KEY_Y, "Y" },
{ LC_KEY_Z, "Z" },
{ LC_KEY_NUMPAD0, "Numpad 0" },
{ LC_KEY_NUMPAD1, "Numpad 1" },
{ LC_KEY_NUMPAD2, "Numpad 2" },
{ LC_KEY_NUMPAD3, "Numpad 3" },
{ LC_KEY_NUMPAD4, "Numpad 4" },
{ LC_KEY_NUMPAD5, "Numpad 5" },
{ LC_KEY_NUMPAD6, "Numpad 6" },
{ LC_KEY_NUMPAD7, "Numpad 7" },
{ LC_KEY_NUMPAD8, "Numpad 8" },
{ LC_KEY_NUMPAD9, "Numpad 9" },
{ LC_KEY_MULTIPLY, "Numpad *" },
{ LC_KEY_ADD, "Numpad +" },
{ LC_KEY_SUBTRACT, "Numpad -" },
{ LC_KEY_DECIMAL, "Numpad ." },
{ LC_KEY_DIVIDE, "Numpad /" },
{ LC_KEY_F1, "F1" },
{ LC_KEY_F2, "F2" },
{ LC_KEY_F3, "F3" },
{ LC_KEY_F4, "F4" },
{ LC_KEY_F5, "F5" },
{ LC_KEY_F6, "F6" },
{ LC_KEY_F7, "F7" },
{ LC_KEY_F8, "F8" },
{ LC_KEY_F9, "F9" },
{ LC_KEY_F10, "F10" },
{ LC_KEY_F11, "F11" },
{ LC_KEY_F12, "F12" },
{ LC_KEY_F13, "F13" },
{ LC_KEY_F14, "F14" },
{ LC_KEY_F15, "F15" },
{ LC_KEY_F16, "F16" },
{ LC_KEY_F17, "F17" },
{ LC_KEY_F18, "F18" },
{ LC_KEY_F19, "F19" },
{ LC_KEY_F20, "F20" },
{ LC_KEY_F21, "F21" },
{ LC_KEY_F22, "F22" },
{ LC_KEY_F23, "F23" },
{ LC_KEY_F24, "F24" },
{ LC_KEY_NUMLOCK, "Num Lock" },
{ LC_KEY_SCROLL, "Scroll" }
};
// Returns a string with the name of the key.
const char* GetKeyName(char Key)
{
int Count = sizeof(KeyNames)/sizeof(KeyNames[0]);
for (int i = 0; i < Count; i++)
{
if (Key == KeyNames[i].Key)
return KeyNames[i].Name;
}
return NULL;
}
char GetKeyFromName(const char* Name)
{
int Count = sizeof(KeyNames)/sizeof(KeyNames[0]);
for (int i = 0; i < Count; i++)
{
if (!strcmp(Name, KeyNames[i].Name))
return KeyNames[i].Key;
}
return 0;
}

151
common/keyboard.h Normal file
View file

@ -0,0 +1,151 @@
#ifndef _KEYBOARD_H_
#define _KEYBOARD_H_
#include "typedefs.h"
// ============================================================================
// Keyboard keys.
#define LC_KEY_BACK 0x08
#define LC_KEY_TAB 0x09
#define LC_KEY_RETURN 0x0D
#define LC_KEY_PAUSE 0x13
#define LC_KEY_CAPITAL 0x14
#define LC_KEY_ESCAPE 0x1B
#define LC_KEY_SPACE 0x20
#define LC_KEY_PRIOR 0x21
#define LC_KEY_NEXT 0x22
#define LC_KEY_END 0x23
#define LC_KEY_HOME 0x24
#define LC_KEY_LEFT 0x25
#define LC_KEY_UP 0x26
#define LC_KEY_RIGHT 0x27
#define LC_KEY_DOWN 0x28
#define LC_KEY_SELECT 0x29
#define LC_KEY_PRINT 0x2A
#define LC_KEY_INSERT 0x2D
#define LC_KEY_DELETE 0x2E
#define LC_KEY_0 0x30
#define LC_KEY_1 0x31
#define LC_KEY_2 0x32
#define LC_KEY_3 0x33
#define LC_KEY_4 0x34
#define LC_KEY_5 0x35
#define LC_KEY_6 0x36
#define LC_KEY_7 0x37
#define LC_KEY_8 0x38
#define LC_KEY_9 0x39
#define LC_KEY_A 0x41
#define LC_KEY_B 0x42
#define LC_KEY_C 0x43
#define LC_KEY_D 0x44
#define LC_KEY_E 0x45
#define LC_KEY_F 0x46
#define LC_KEY_G 0x47
#define LC_KEY_H 0x48
#define LC_KEY_I 0x49
#define LC_KEY_J 0x4A
#define LC_KEY_K 0x4B
#define LC_KEY_L 0x4C
#define LC_KEY_M 0x4D
#define LC_KEY_N 0x4E
#define LC_KEY_O 0x4F
#define LC_KEY_P 0x50
#define LC_KEY_Q 0x51
#define LC_KEY_R 0x52
#define LC_KEY_S 0x53
#define LC_KEY_T 0x54
#define LC_KEY_U 0x55
#define LC_KEY_V 0x56
#define LC_KEY_W 0x57
#define LC_KEY_X 0x58
#define LC_KEY_Y 0x59
#define LC_KEY_Z 0x5A
#define LC_KEY_NUMPAD0 0x60
#define LC_KEY_NUMPAD1 0x61
#define LC_KEY_NUMPAD2 0x62
#define LC_KEY_NUMPAD3 0x63
#define LC_KEY_NUMPAD4 0x64
#define LC_KEY_NUMPAD5 0x65
#define LC_KEY_NUMPAD6 0x66
#define LC_KEY_NUMPAD7 0x67
#define LC_KEY_NUMPAD8 0x68
#define LC_KEY_NUMPAD9 0x69
#define LC_KEY_MULTIPLY 0x6A
#define LC_KEY_ADD 0x6B
//#define LC_KEY_SEPARATOR 0x6C
#define LC_KEY_SUBTRACT 0x6D
#define LC_KEY_DECIMAL 0x6E
#define LC_KEY_DIVIDE 0x6F
#define LC_KEY_F1 0x70
#define LC_KEY_F2 0x71
#define LC_KEY_F3 0x72
#define LC_KEY_F4 0x73
#define LC_KEY_F5 0x74
#define LC_KEY_F6 0x75
#define LC_KEY_F7 0x76
#define LC_KEY_F8 0x77
#define LC_KEY_F9 0x78
#define LC_KEY_F10 0x79
#define LC_KEY_F11 0x7A
#define LC_KEY_F12 0x7B
#define LC_KEY_F13 0x7C
#define LC_KEY_F14 0x7D
#define LC_KEY_F15 0x7E
#define LC_KEY_F16 0x7F
#define LC_KEY_F17 0x80
#define LC_KEY_F18 0x81
#define LC_KEY_F19 0x82
#define LC_KEY_F20 0x83
#define LC_KEY_F21 0x84
#define LC_KEY_F22 0x85
#define LC_KEY_F23 0x86
#define LC_KEY_F24 0x87
#define LC_KEY_NUMLOCK 0x90
#define LC_KEY_SCROLL 0x91
// ============================================================================
// Functions.
#define LC_KEYMOD1_SHIFT 0x01
#define LC_KEYMOD1_CONTROL 0x02
#define LC_KEYMOD2_SHIFT 0x04
#define LC_KEYMOD2_CONTROL 0x08
#define LC_KEYMOD_VIEWONLY 0x10
#define LC_KEYMOD1_MASK (LC_KEYMOD1_SHIFT | LC_KEYMOD1_CONTROL)
#define LC_KEYMOD2_MASK (LC_KEYMOD2_SHIFT | LC_KEYMOD2_CONTROL)
#define LC_KEYMOD_MASK (LC_KEYMOD1_MASK | LC_KEYMOD2_MASK)
#define LC_KEYMOD_1TO2(a) ((a & ~LC_KEYMOD_MASK) | ((a & LC_KEYMOD1_MASK) << 2))
#define LC_KEYMOD_2TO1(a) ((a & ~LC_KEYMOD_MASK) | ((a & LC_KEYMOD2_MASK) >> 2))
typedef struct
{
LC_COMMANDS ID;
const char* Description;
unsigned char Flags;
unsigned char Key1;
unsigned char Key2;
} LC_KEYBOARD_COMMAND;
extern LC_KEYBOARD_COMMAND KeyboardShortcuts[];
extern const int KeyboardShortcutsCount;
const char* GetKeyName(char Key);
char GetKeyFromName(const char* Name);
void InitKeyboardShortcuts();
void ResetKeyboardShortcuts();
bool SaveKeyboardShortcuts(const char* FileName);
bool LoadKeyboardShortcuts(const char* FileName);
#endif // _KEYBOARD_H_

407
common/lc_application.cpp Normal file
View file

@ -0,0 +1,407 @@
#include <stdio.h>
#include "lc_application.h"
#include "library.h"
#include "system.h"
#include "console.h"
#include "config.h"
#include "opengl.h"
#include "project.h"
#include "image.h"
// ----------------------------------------------------------------------------
// Global functions.
lcApplication* g_App;
PiecesLibrary* lcGetPiecesLibrary()
{
LC_ASSERT(g_App, "g_App not initialized.");
return g_App->GetPiecesLibrary();
}
Project* lcGetActiveProject()
{
LC_ASSERT(g_App, "g_App not initialized.");
return g_App->GetActiveProject();
}
// ----------------------------------------------------------------------------
// lcApplication class.
lcApplication::lcApplication()
{
m_ActiveProject = NULL;
m_Library = NULL;
}
lcApplication::~lcApplication()
{
}
void lcApplication::AddProject(Project* project)
{
m_Projects.Add(project);
if (m_ActiveProject == NULL)
m_ActiveProject = project;
}
bool lcApplication::LoadPiecesLibrary(const char* LibPath, const char* SysLibPath)
{
// Create an empty library.
if (m_Library == NULL)
m_Library = new PiecesLibrary();
else
m_Library->Unload();
// Check if the user specified a library path in the command line.
if (LibPath != NULL)
if (m_Library->Load(LibPath))
return true;
// Check for the LEOCAD_LIB environment variable.
char* EnvPath = getenv("LEOCAD_LIB");
if (EnvPath != NULL)
if (m_Library->Load(EnvPath))
return true;
// Try the executable install path last.
if (SysLibPath != NULL)
if (m_Library->Load(SysLibPath))
return true;
#ifdef LC_WINDOWS
SystemDoMessageBox("Cannot load pieces library.\n"
"Make sure that you have the PIECES.IDX file in the same "
"folder where you installed the program.", LC_MB_OK|LC_MB_ICONERROR);
#else
printf("Error: Cannot load pieces library.\n");
#endif
return false;
}
void lcApplication::ParseIntegerArgument(int* CurArg, int argc, char* argv[], int* Value)
{
if (argc > (*CurArg + 1))
{
(*CurArg)++;
int val;
if ((sscanf(argv[(*CurArg)], "%d", &val) == 1) && (val > 0))
*Value = val;
else
console.PrintWarning("Invalid value specified for the %s argument.", argv[(*CurArg) - 1]);
}
else
{
console.PrintWarning("Not enough parameters for the %s argument.", argv[(*CurArg) - 1]);
}
}
void lcApplication::ParseStringArgument(int* CurArg, int argc, char* argv[], char** Value)
{
if (argc > (*CurArg + 1))
{
(*CurArg)++;
*Value = argv[(*CurArg)];
}
else
{
console.PrintWarning("No path specified after the %s argument.", argv[(*CurArg) - 1]);
}
}
bool lcApplication::Initialize(int argc, char* argv[], const char* SysLibPath)
{
// System setup parameters.
char* LibPath = NULL;
char* GLPath = NULL;
// Image output options.
bool SaveImage = false;
bool ImageAnimation = false;
bool ImageInstructions = false;
bool ImageHighlight = false;
int ImageWidth = Sys_ProfileLoadInt("Default", "Image Width", 640);
int ImageHeight = Sys_ProfileLoadInt("Default", "Image Height", 480);
int ImageStart = 0;
int ImageEnd = 0;
char* ImageName = NULL;
// File to open.
char* ProjectName = NULL;
// Parse the command line arguments.
for (int i = 1; i < argc; i++)
{
char* Param = argv[i];
if (Param[0] == '-')
{
if (strcmp(Param, "--libgl") == 0)
{
ParseStringArgument(&i, argc, argv, &GLPath);
}
else if ((strcmp(Param, "-l") == 0) || (strcmp(Param, "--libpath") == 0))
{
ParseStringArgument(&i, argc, argv, &LibPath);
}
else if ((strcmp(Param, "-i") == 0) || (strcmp(Param, "--image") == 0))
{
SaveImage = true;
if ((argc > (i+1)) && (argv[i+1][0] != '-'))
{
i++;
ImageName = argv[i];
}
}
else if ((strcmp(Param, "-w") == 0) || (strcmp(Param, "--width") == 0))
{
ParseIntegerArgument(&i, argc, argv, &ImageWidth);
}
else if ((strcmp(Param, "-h") == 0) || (strcmp(Param, "--height") == 0))
{
ParseIntegerArgument(&i, argc, argv, &ImageHeight);
}
else if ((strcmp(Param, "-f") == 0) || (strcmp(Param, "--from") == 0))
{
ParseIntegerArgument(&i, argc, argv, &ImageStart);
}
else if ((strcmp(Param, "-t") == 0) || (strcmp(Param, "--to") == 0))
{
ParseIntegerArgument(&i, argc, argv, &ImageEnd);
}
else if (strcmp(Param, "--animation") == 0)
ImageAnimation = true;
else if (strcmp(Param, "--instructions") == 0)
ImageInstructions = true;
else if (strcmp(Param, "--highlight") == 0)
ImageHighlight = true;
else if ((strcmp(Param, "-v") == 0) || (strcmp(Param, "--version") == 0))
{
printf("LeoCAD version " LC_VERSION_TEXT LC_VERSION_TAG " for "LC_VERSION_OSNAME"\n");
printf("Copyright (c) 1996-2006, BT Software\n");
printf("Compiled "__DATE__"\n");
#ifdef LC_HAVE_JPEGLIB
printf("With JPEG support\n");
#else
printf("Without JPEG support\n");
#endif
#ifdef LC_HAVE_PNGLIB
printf("With PNG support\n");
#else
printf("Without PNG support\n");
#endif
return false;
}
else if ((strcmp(Param, "-?") == 0) || (strcmp(Param, "--help") == 0))
{
printf("Usage: leocad [options] [file]\n");
printf(" [options] can be:\n");
printf(" --libgl <path>: Searches for OpenGL libraries in path.\n");
printf(" --libpath <path>: Loads the Pieces library from path.\n");
printf(" -i, --image <outfile.ext>: Saves a picture in the format specified by ext.\n");
printf(" -w, --width <width>: Sets the picture width.\n");
printf(" -h, --height <height>: Sets the picture height.\n");
printf(" -f, --from <time>: Sets the first frame or step to save pictures.\n");
printf(" -t, --to <time>: Sets the last frame or step to save pictures.\n");
printf(" --animation: Saves animations frames.\n");
printf(" --instructions: Saves instructions steps.\n");
printf(" --highlight: Highlight pieces in the steps they appear.\n");
printf(" \n");
}
else
console.PrintWarning("Unknown parameter: %s\n", Param);
}
else
{
ProjectName = Param;
}
}
// Initialize other systems.
if (!GL_Initialize(GLPath))
return false;
if (!LoadPiecesLibrary(LibPath, SysLibPath))
return false;
SystemInit();
// Create a new project.
Project* project = new Project();
AddProject(project);
// Load project.
if (ProjectName && project->OpenProject(ProjectName))
{
if (!SaveImage)
return true;
// Check if there's a file name and it has an extension.
bool NeedExt = true;
String FileName;
if (!ImageName)
{
FileName = ProjectName;
int i = FileName.ReverseFind('.');
if (i != -1)
FileName[i] = 0;
}
else
{
FileName = ImageName;
int i = FileName.ReverseFind('.');
String Ext;
if (i != -1)
{
Ext = FileName.Right(FileName.GetLength() - i);
Ext.MakeLower();
}
if ((Ext == "bmp") || (Ext == "gif"))
NeedExt = false;
#ifdef LC_HAVE_JPEGLIB
else if ((Ext == "jpg") || (Ext == "jpeg"))
NeedExt = false;
#endif
#ifdef LC_HAVE_PNGLIB
else if (Ext == "png")
NeedExt = false;
#endif
}
// Setup default options.
LC_IMAGE_OPTS ImageOptions;
unsigned long image = Sys_ProfileLoadInt ("Default", "Image Options", 1|LC_IMAGE_TRANSPARENT);
ImageOptions.quality = Sys_ProfileLoadInt ("Default", "JPEG Quality", 70);
ImageOptions.interlaced = (image & LC_IMAGE_PROGRESSIVE) != 0;
ImageOptions.transparent = (image & LC_IMAGE_TRANSPARENT) != 0;
ImageOptions.truecolor = (image & LC_IMAGE_HIGHCOLOR) != 0;
ImageOptions.format = image & ~(LC_IMAGE_MASK);
ImageOptions.background[0] = (unsigned char)(project->GetBackgroundColor()[0]*255);
ImageOptions.background[1] = (unsigned char)(project->GetBackgroundColor()[1]*255);
ImageOptions.background[2] = (unsigned char)(project->GetBackgroundColor()[2]*255);
// Append file extension if needed.
if (NeedExt)
{
switch (ImageOptions.format)
{
default:
case LC_IMAGE_BMP:
FileName += ".bmp";
break;
case LC_IMAGE_GIF:
FileName += ".gif";
break;
case LC_IMAGE_JPG:
FileName += ".jpg";
break;
case LC_IMAGE_PNG:
FileName += ".png";
break;
}
}
if (ImageInstructions)
project->SetAnimation(false);
else if (ImageAnimation)
project->SetAnimation(true);
if (ImageEnd < ImageStart)
ImageEnd = ImageStart;
else if (ImageStart > ImageEnd)
ImageStart = ImageEnd;
if ((ImageStart == 0) && (ImageEnd == 0))
{
ImageStart = ImageEnd = project->GetCurrentTime();
}
else if ((ImageStart == 0) && (ImageEnd != 0))
{
ImageStart = ImageEnd;
}
else if ((ImageStart != 0) && (ImageEnd == 0))
{
ImageEnd = ImageStart;
}
if (project->IsAnimation())
{
if (ImageStart > project->GetTotalFrames())
ImageStart = project->GetTotalFrames();
if (ImageEnd > project->GetTotalFrames())
ImageEnd = project->GetTotalFrames();
}
else
{
if (ImageStart > 255)
ImageStart = 255;
if (ImageEnd > 255)
ImageEnd = 255;
}
Image* images = new Image[ImageEnd - ImageStart + 1];
project->CreateImages(images, ImageWidth, ImageHeight, ImageStart, ImageEnd, ImageHighlight);
for (int i = 0; i <= ImageEnd - ImageStart; i++)
{
char idx[256];
String Frame;
if (ImageStart != ImageEnd)
{
sprintf(idx, "%02d", i+1);
int Ext = FileName.ReverseFind('.');
Frame = FileName.Left(Ext) + idx + FileName.Right(FileName.GetLength() - Ext);
}
else
Frame = FileName;
images[i].FileSave(Frame, &ImageOptions);
}
delete []images;
return false;
}
else
{
if (SaveImage)
return false;
else
project->OnNewDocument();
}
return true;
}
void lcApplication::Shutdown()
{
for (int i = 0; i < m_Projects.GetSize(); i++)
{
Project* project = m_Projects[i];
project->HandleNotify(LC_ACTIVATE, 0);
delete project;
}
delete m_Library;
m_Library = NULL;
GL_Shutdown();
}

51
common/lc_application.h Normal file
View file

@ -0,0 +1,51 @@
#ifndef _LC_APPLICATION_H_
#define _LC_APPLICATION_H_
#include "array.h"
class Project;
class PiecesLibrary;
class lcApplication
{
public:
lcApplication();
~lcApplication();
bool Initialize(int argc, char *argv[], const char* SysLibPath);
void Shutdown();
// Pieces library.
bool LoadPiecesLibrary(const char* LibPath, const char* SysLibPath);
PiecesLibrary* GetPiecesLibrary() const
{
return m_Library;
}
// Projects.
void AddProject(Project* project);
Project* GetActiveProject() const
{
return m_ActiveProject;
}
void SetActiveProject(Project* project)
{
m_ActiveProject = project;
}
protected:
void ParseIntegerArgument(int* CurArg, int argc, char* argv[], int* Value);
void ParseStringArgument(int* CurArg, int argc, char* argv[], char** Value);
Project* m_ActiveProject;
PtrArray<Project> m_Projects;
PiecesLibrary* m_Library;
};
extern lcApplication* g_App;
PiecesLibrary* lcGetPiecesLibrary();
Project* lcGetActiveProject();
#endif // _LC_APPLICATION_H_

2749
common/library.cpp Executable file

File diff suppressed because it is too large Load diff

168
common/library.h Executable file
View file

@ -0,0 +1,168 @@
#ifndef _LIBRARY_H_
#define _LIBRARY_H_
#include "defines.h"
#include "str.h"
#include "array.h"
class File;
class FileDisk;
class Texture;
class PieceInfo;
#define LC_CATEGORY_FILE_ID LC_FOURCC('C', 'A', 'T', 0)
#define LC_CATEGORY_FILE_VERSION 0x0100
typedef struct
{
String Name;
String Keywords;
} PiecesLibraryCategory;
class PiecesLibrary
{
public:
PiecesLibrary();
~PiecesLibrary();
const char* GetLibraryPath() const
{ return m_LibraryPath; }
int GetPieceCount () const
{ return m_nPieceCount; }
int GetTextureCount () const
{ return m_nTextureCount; }
// Categories.
bool PieceInCategory(PieceInfo* Info, const String& CategoryKeywords) const;
int GetFirstCategory(PieceInfo* Info) const;
void GetCategoryEntries(int CategoryIndex, bool GroupPieces, PtrArray<PieceInfo>& SinglePieces, PtrArray<PieceInfo>& GroupedPieces) const;
void GetPatternedPieces(PieceInfo* Parent, PtrArray<PieceInfo>& Pieces) const;
void SetCategory(int Index, const String& Name, const String& Keywords);
void AddCategory(const String& Name, const String& Keywords);
void RemoveCategory(int Index);
void ResetCategories();
bool SaveCategories();
bool DoSaveCategories(bool AskName);
bool LoadCategories(const char* FileName);
const String& GetCategoryName(int Index) const
{ return m_Categories[Index].Name; }
const String& GetCategoryKeywords(int Index) const
{ return m_Categories[Index].Keywords; }
int GetNumCategories() const
{ return m_Categories.GetSize(); }
int FindCategoryIndex(const String& CategoryName) const
{
for (int i = 0; i < m_Categories.GetSize(); i++)
if (m_Categories[i].Name == CategoryName)
return i;
return -1;
}
bool Load(const char* libpath);
void Unload();
// Search for pieces.
PieceInfo* FindPieceInfo(const char* name) const;
PieceInfo* GetPieceInfo(int index) const;
int GetPieceIndex(PieceInfo *pInfo) const;
Texture* FindTexture(const char* name) const;
Texture* GetTexture(int index) const;
// File operations.
bool DeletePieces(PtrArray<PieceInfo>& Pieces);
bool LoadUpdate(const char* update);
bool DeleteTextures(char** Names, int NumTextures);
bool ImportTexture(const char* Name);
bool ImportLDrawPiece(const char* Filename);
// Set when pieces are added/removed from the library.
bool m_Modified;
protected:
char m_LibraryPath[LC_MAXPATH]; // path to the library files
int m_nMovedCount; // number of moved pieces
char* m_pMovedReference; // moved pieces list
int m_nPieceCount; // number of pieces
PieceInfo* m_pPieceIdx; // pieces array
int m_nTextureCount; // number of textures
Texture* m_pTextures; // textures array
// Categories.
ObjArray<PiecesLibraryCategory> m_Categories;
bool m_CategoriesModified;
char m_CategoriesFile[LC_MAXPATH];
bool ValidatePiecesFile(FileDisk& IdxFile, FileDisk& BinFile) const;
bool ValidateTexturesFile(File& IdxFile, File& BinFile) const;
// File headers
static const char PiecesBinHeader[32];
static const char PiecesIdxHeader[32];
static const int PiecesFileVersion;
static const char TexturesBinHeader[32];
static const char TexturesIdxHeader[32];
static const int TexturesFileVersion;
};
// ============================================================================
// This should be cleaned and moved to the PiecesLibrary class
typedef struct connection_s
{
unsigned char type;
float pos[3];
float up[3];
connection_s* next;
} connection_t;
typedef struct group_s
{
connection_t* connections[5];
void* drawinfo;
unsigned long infosize;
group_s* next;
} group_t;
typedef struct lineinfo_s
{
unsigned char type;
unsigned char color;
float points[12];
lineinfo_s* next;
} lineinfo_t;
typedef struct texture_s
{
float points[20];
unsigned char color;
char name[9];
texture_s* next;
} texture_t;
struct LC_LDRAW_PIECE
{
float* verts;
unsigned int verts_count;
bool long_info;
connection_t* connections;
group_t* groups;
texture_t* textures;
char name[LC_MAXPATH];
char description[65];
};
bool ReadLDrawPiece(const char* filename, LC_LDRAW_PIECE* piece);
bool SaveLDrawPiece(LC_LDRAW_PIECE* piece);
void FreeLDrawPiece(LC_LDRAW_PIECE* piece);
#endif // _LIBRARY_H_

573
common/light.cpp Normal file
View file

@ -0,0 +1,573 @@
// Light object.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include "light.h"
#include "defines.h"
#include "globals.h"
#include "vector.h"
#include "matrix.h"
GLuint Light::m_nSphereList = 0;
GLuint Light::m_nTargetList = 0;
static LC_OBJECT_KEY_INFO light_key_info[LC_LK_COUNT] =
{
{ "Light Position", 3, LC_LK_POSITION },
{ "Light Target", 3, LC_LK_TARGET },
{ "Ambient Color", 3, LC_LK_AMBIENT },
{ "Diffuse Color", 3, LC_LK_DIFFUSE },
{ "Specular Color", 3, LC_LK_SPECULAR },
{ "Constant Attenuation", 1, LC_LK_CONSTANT },
{ "Linear Attenuation", 1, LC_LK_LINEAR },
{ "Quadratic Attenuation", 1, LC_LK_QUADRATIC },
{ "Spot Cutoff", 1, LC_LK_CUTOFF },
{ "Spot Exponent", 1, LC_LK_EXPONENT }
};
// =============================================================================
// CameraTarget class
LightTarget::LightTarget (Light *pParent)
: Object (LC_OBJECT_LIGHT_TARGET)
{
m_pParent = pParent;
/*
strcpy (m_strName, pParent->GetName ());
m_strName[LC_OBJECT_NAME_LEN-8] = '\0';
strcat (m_strName, ".Target");
*/
}
LightTarget::~LightTarget ()
{
}
void LightTarget::MinIntersectDist (LC_CLICKLINE* pLine)
{
float dist = (float)BoundingBoxIntersectDist (pLine);
if (dist < pLine->mindist)
{
pLine->mindist = dist;
pLine->pClosest = this;
}
}
void LightTarget::Select (bool bSelecting, bool bFocus, bool bMultiple)
{
m_pParent->SelectTarget (bSelecting, bFocus, bMultiple);
}
const char* LightTarget::GetName() const
{
return m_pParent->GetName();
}
// =============================================================================
// Light class
// New positional light
Light::Light (float px, float py, float pz)
: Object (LC_OBJECT_LIGHT)
{
Initialize ();
float pos[] = { px, py, pz }, target[] = { 0, 0, 0 };
ChangeKey (1, false, true, pos, LC_LK_POSITION);
ChangeKey (1, false, true, target, LC_LK_TARGET);
ChangeKey (1, true, true, pos, LC_LK_POSITION);
ChangeKey (1, true, true, target, LC_LK_TARGET);
m_fPos[3] = 0.0f;
UpdatePosition (1, false);
}
// New directional light
Light::Light (float px, float py, float pz, float tx, float ty, float tz)
: Object (LC_OBJECT_LIGHT)
{
Initialize ();
float pos[] = { px, py, pz }, target[] = { tx, ty, tz };
ChangeKey (1, false, true, pos, LC_LK_POSITION);
ChangeKey (1, false, true, target, LC_LK_TARGET);
ChangeKey (1, true, true, pos, LC_LK_POSITION);
ChangeKey (1, true, true, target, LC_LK_TARGET);
m_pTarget = new LightTarget (this);
m_fPos[3] = 1.0f;
UpdatePosition (1, false);
}
void Light::Initialize ()
{
m_bEnabled = true;
m_pNext = NULL;
m_nState = 0;
m_pTarget = NULL;
m_nList = 0;
memset (m_strName, 0, sizeof (m_strName));
m_fAmbient[3] = 1.0f;
m_fDiffuse[3] = 1.0f;
m_fSpecular[3] = 1.0f;
float *values[] = { m_fPos, m_fTarget, m_fAmbient, m_fDiffuse, m_fSpecular,
&m_fConstant, &m_fLinear, &m_fQuadratic, &m_fCutoff, &m_fExponent };
RegisterKeys (values, light_key_info, LC_LK_COUNT);
// set the default values
float ambient[] = { 0, 0, 0 }, diffuse[] = { 0.8f, 0.8f, 0.8f }, specular[] = { 1, 1, 1 };
float constant = 1, linear = 0, quadratic = 0, cutoff = 30, exponent = 0;
ChangeKey (1, false, true, ambient, LC_LK_AMBIENT);
ChangeKey (1, false, true, diffuse, LC_LK_DIFFUSE);
ChangeKey (1, false, true, specular, LC_LK_SPECULAR);
ChangeKey (1, false, true, &constant, LC_LK_CONSTANT);
ChangeKey (1, false, true, &linear, LC_LK_LINEAR);
ChangeKey (1, false, true, &quadratic, LC_LK_QUADRATIC);
ChangeKey (1, false, true, &cutoff, LC_LK_CUTOFF);
ChangeKey (1, false, true, &exponent, LC_LK_EXPONENT);
ChangeKey (1, true, true, ambient, LC_LK_AMBIENT);
ChangeKey (1, true, true, diffuse, LC_LK_DIFFUSE);
ChangeKey (1, true, true, specular, LC_LK_SPECULAR);
ChangeKey (1, true, true, &constant, LC_LK_CONSTANT);
ChangeKey (1, true, true, &linear, LC_LK_LINEAR);
ChangeKey (1, true, true, &quadratic, LC_LK_QUADRATIC);
ChangeKey (1, true, true, &cutoff, LC_LK_CUTOFF);
ChangeKey (1, true, true, &exponent, LC_LK_EXPONENT);
}
Light::~Light ()
{
if (m_nList != 0)
glDeleteLists (m_nList, 1);
delete m_pTarget;
}
void Light::CreateName(const Light* pLight)
{
int i, max = 0;
for (; pLight; pLight = pLight->m_pNext)
{
if (strncmp (pLight->m_strName, "Light ", 6) == 0)
{
if (sscanf(pLight->m_strName + 6, " #%d", &i) == 1)
{
if (i > max)
max = i;
}
}
}
sprintf (m_strName, "Light #%.2d", max+1);
}
void Light::Select (bool bSelecting, bool bFocus, bool bMultiple)
{
if (bSelecting == true)
{
if (bFocus == true)
{
m_nState |= (LC_LIGHT_FOCUSED|LC_LIGHT_SELECTED);
if (m_pTarget != NULL)
m_pTarget->Select (false, true, bMultiple);
}
else
m_nState |= LC_LIGHT_SELECTED;
if (bMultiple == false)
if (m_pTarget != NULL)
m_pTarget->Select (false, false, bMultiple);
}
else
{
if (bFocus == true)
m_nState &= ~(LC_LIGHT_FOCUSED);
else
m_nState &= ~(LC_LIGHT_SELECTED|LC_LIGHT_FOCUSED);
}
}
void Light::SelectTarget (bool bSelecting, bool bFocus, bool bMultiple)
{
// FIXME: the target should handle this
if (bSelecting == true)
{
if (bFocus == true)
{
m_nState |= (LC_LIGHT_TARGET_FOCUSED|LC_LIGHT_TARGET_SELECTED);
Select (false, true, bMultiple);
}
else
m_nState |= LC_LIGHT_TARGET_SELECTED;
if (bMultiple == false)
Select (false, false, bMultiple);
}
else
{
if (bFocus == true)
m_nState &= ~(LC_LIGHT_TARGET_FOCUSED);
else
m_nState &= ~(LC_LIGHT_TARGET_SELECTED|LC_LIGHT_TARGET_FOCUSED);
}
}
void Light::MinIntersectDist (LC_CLICKLINE* pLine)
{
float dist;
if (m_nState & LC_LIGHT_HIDDEN)
return;
dist = (float)BoundingBoxIntersectDist (pLine);
if (dist < pLine->mindist)
{
pLine->mindist = dist;
pLine->pClosest = this;
}
if (m_pTarget != NULL)
m_pTarget->MinIntersectDist (pLine);
}
void Light::Move (unsigned short nTime, bool bAnimation, bool bAddKey, float dx, float dy, float dz)
{
if (IsEyeSelected())
{
m_fPos[0] += dx;
m_fPos[1] += dy;
m_fPos[2] += dz;
ChangeKey (nTime, bAnimation, bAddKey, m_fPos, LC_LK_POSITION);
}
if (IsTargetSelected())
{
m_fTarget[0] += dx;
m_fTarget[1] += dy;
m_fTarget[2] += dz;
ChangeKey (nTime, bAnimation, bAddKey, m_fTarget, LC_LK_TARGET);
}
}
void Light::UpdatePosition (unsigned short nTime, bool bAnimation)
{
CalculateKeys (nTime, bAnimation);
BoundingBoxCalculate (m_fPos);
if (m_pTarget != NULL)
{
m_pTarget->BoundingBoxCalculate (m_fTarget);
if (m_nList == 0)
m_nList = glGenLists(1);
glNewList (m_nList, GL_COMPILE);
glPushMatrix ();
glTranslatef (m_fPos[0], m_fPos[1], m_fPos[2]);
Vector frontvec (m_fTarget[0]-m_fPos[0], m_fTarget[1]-m_fPos[1], m_fTarget[2]-m_fPos[2]);
float len = frontvec.Length (), up[3] = { 1, 1, 1 };
if (fabs (frontvec[0]) < fabs (frontvec[1]))
{
if (fabs (frontvec[0]) < fabs (frontvec[2]))
up[0] = -(up[1]*frontvec[1] + up[2]*frontvec[2]);
else
up[2] = -(up[0]*frontvec[0] + up[1]*frontvec[1]);
}
else
{
if (fabs (frontvec[1]) < fabs (frontvec[2]))
up[1] = -(up[0]*frontvec[0] + up[2]*frontvec[2]);
else
up[2] = -(up[0]*frontvec[0] + up[1]*frontvec[1]);
}
Matrix mat;
mat.CreateLookat (m_fPos, m_fTarget, up);
mat.Invert ();
mat.SetTranslation (0, 0, 0);
glMultMatrixf (mat.m);
glEnableClientState (GL_VERTEX_ARRAY);
float verts[16*3];
for (int i = 0; i < 8; i++)
{
verts[i*6] = verts[i*6+3] = (float)cos ((float)i/4 * PI) * 0.3f;
verts[i*6+1] = verts[i*6+4] = (float)sin ((float)i/4 * PI) * 0.3f;
verts[i*6+2] = 0.3f;
verts[i*6+5] = -0.3f;
}
glVertexPointer (3, GL_FLOAT, 0, verts);
glDrawArrays (GL_LINES, 0, 16);
glVertexPointer (3, GL_FLOAT, 6*sizeof(float), verts);
glDrawArrays (GL_LINE_LOOP, 0, 8);
glVertexPointer (3, GL_FLOAT, 6*sizeof(float), &verts[3]);
glDrawArrays (GL_LINE_LOOP, 0, 8);
glBegin (GL_LINE_LOOP);
glVertex3f (-0.5f, -0.5f, -0.3f);
glVertex3f ( 0.5f, -0.5f, -0.3f);
glVertex3f ( 0.5f, 0.5f, -0.3f);
glVertex3f (-0.5f, 0.5f, -0.3f);
glEnd ();
glTranslatef(0, 0, -len);
glEndList();
if (m_nTargetList == 0)
{
m_nTargetList = glGenLists (1);
glNewList (m_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 ();
}
}
else
{
if (m_nSphereList == 0)
m_nSphereList = glGenLists (1);
glNewList (m_nSphereList, GL_COMPILE);
const float radius = 0.2f;
const int slices = 6, stacks = 6;
float rho, drho, theta, dtheta;
float x, y, z;
int i, 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 ();
}
}
void Light::Render (float fLineWidth)
{
if (m_pTarget != NULL)
{
if (IsEyeSelected())
{
glLineWidth(fLineWidth*2);
glColor3ubv(FlatColorArray[(m_nState & LC_LIGHT_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);
glColor3ubv(FlatColorArray[(m_nState & LC_LIGHT_TARGET_FOCUSED) != 0 ? LC_COL_FOCUSED : LC_COL_SELECTED]);
glCallList(m_nTargetList);
glLineWidth(fLineWidth);
}
else
{
glColor3f(0.5f, 0.8f, 0.5f);
glCallList(m_nTargetList);
}
glColor3f(0.5f, 0.8f, 0.5f);
glBegin(GL_LINES);
glVertex3fv(m_fPos);
glVertex3fv(m_fTarget);
glEnd();
if (IsSelected())
{
Matrix projection, modelview;
Vector frontvec(m_fTarget[0]-m_fPos[0], m_fTarget[1]-m_fPos[1], m_fTarget[2]-m_fPos[2]);
float len = frontvec.Length (), up[3] = { 1, 1, 1 };
if (fabs (frontvec[0]) < fabs (frontvec[1]))
{
if (fabs (frontvec[0]) < fabs (frontvec[2]))
up[0] = -(up[1]*frontvec[1] + up[2]*frontvec[2]);
else
up[2] = -(up[0]*frontvec[0] + up[1]*frontvec[1]);
}
else
{
if (fabs (frontvec[1]) < fabs (frontvec[2]))
up[1] = -(up[0]*frontvec[0] + up[2]*frontvec[2]);
else
up[2] = -(up[0]*frontvec[0] + up[1]*frontvec[1]);
}
glPushMatrix ();
modelview.CreateLookat (m_fPos, m_fTarget, up);
modelview.Invert ();
glMultMatrixf (modelview.m);
projection.CreatePerspective (2*m_fCutoff, 1.0f, 0.01f, len);
projection.Invert ();
glMultMatrixf (projection.m);
// draw the viewing frustum
glBegin (GL_LINE_LOOP);
glVertex3f ( 0.5f, 1.0f, 1.0f);
glVertex3f ( 1.0f, 0.5f, 1.0f);
glVertex3f ( 1.0f, -0.5f, 1.0f);
glVertex3f ( 0.5f, -1.0f, 1.0f);
glVertex3f (-0.5f, -1.0f, 1.0f);
glVertex3f (-1.0f, -0.5f, 1.0f);
glVertex3f (-1.0f, 0.5f, 1.0f);
glVertex3f (-0.5f, 1.0f, 1.0f);
glEnd ();
glBegin (GL_LINES);
glVertex3f (1, 1, -1);
glVertex3f (0.75f, 0.75f, 1);
glVertex3f (-1, 1, -1);
glVertex3f (-0.75f, 0.75f, 1);
glVertex3f (-1, -1, -1);
glVertex3f (-0.75f, -0.75f, 1);
glVertex3f (1, -1, -1);
glVertex3f (0.75f, -0.75f, 1);
glEnd ();
glPopMatrix();
}
}
else
{
glPushMatrix ();
glTranslatef (m_fPos[0], m_fPos[1], m_fPos[2]);
if (IsEyeSelected ())
{
glLineWidth (fLineWidth*2);
glColor3ubv (FlatColorArray[(m_nState & LC_LIGHT_FOCUSED) != 0 ? LC_COL_FOCUSED : LC_COL_SELECTED]);
glCallList (m_nSphereList);
glLineWidth (fLineWidth);
}
else
{
glColor3f (0.5f, 0.8f, 0.5f);
glCallList (m_nSphereList);
}
glPopMatrix ();
}
}
void Light::Setup (int index)
{
GLenum light = (GLenum)(GL_LIGHT0+index);
if (!m_bEnabled)
{
glDisable (light);
return;
}
glEnable (light);
glLightfv (light, GL_POSITION, m_fPos);
glLightfv (light, GL_AMBIENT, m_fAmbient);
glLightfv (light, GL_DIFFUSE, m_fDiffuse);
glLightfv (light, GL_SPECULAR, m_fSpecular);
glLightf (light, GL_CONSTANT_ATTENUATION, m_fConstant);
glLightf (light, GL_LINEAR_ATTENUATION, m_fLinear);
glLightf (light, GL_QUADRATIC_ATTENUATION, m_fQuadratic);
if (m_pTarget != NULL)
{
Vector dir (m_fTarget[0]-m_fPos[0], m_fTarget[1]-m_fPos[1], m_fTarget[2]-m_fPos[2]);
dir.Normalize ();
glLightf (light, GL_SPOT_CUTOFF, m_fCutoff);
glLightf (light, GL_SPOT_EXPONENT, m_fExponent);
glLightfv (light, GL_SPOT_DIRECTION, dir);
}
}

138
common/light.h Normal file
View file

@ -0,0 +1,138 @@
#ifndef _LIGHT_H_
#define _LIGHT_H_
#include "opengl.h"
#include "object.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;
class LightTarget;
typedef enum
{
LC_LK_POSITION, LC_LK_TARGET, // position
LC_LK_AMBIENT, LC_LK_DIFFUSE, LC_LK_SPECULAR, // color
LC_LK_CONSTANT, LC_LK_LINEAR, LC_LK_QUADRATIC, // attenuation
LC_LK_CUTOFF, LC_LK_EXPONENT, // spot
LC_LK_COUNT
} LC_LK_TYPES;
class LightTarget : public Object
{
public:
LightTarget (Light *pParent);
~LightTarget ();
public:
void MinIntersectDist (LC_CLICKLINE* pLine);
bool IntersectsVolume(const Vector4* Planes, int NumPlanes)
{ return false; }
void Select (bool bSelecting, bool bFocus, bool bMultiple);
void Move (unsigned short nTime, bool bAnimation, bool bAddKey, float x, float y, float z)
{
// FIXME: move the position handling to the light target
}
const char* GetName() const;
Light* GetParent () const
{ return m_pParent; }
protected:
Light* m_pParent;
friend class Light; // FIXME: needed for BoundingBoxCalculate ()
// remove and use UpdatePosition instead
};
class Light : public Object
{
public:
Light (float px, float py, float pz);
Light (float px, float py, float pz, float tx, float ty, float tz);
virtual ~Light ();
void Select (bool bSelecting, bool bFocus, bool bMultiple);
void SelectTarget (bool bSelecting, bool bFocus, bool bMultiple);
public:
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; }
bool IsEyeSelected()
{ return (m_nState & LC_LIGHT_SELECTED) != 0; }
bool IsTargetSelected()
{ return (m_nState & LC_LIGHT_TARGET_SELECTED) != 0; }
bool IsEyeFocused()
{ return (m_nState & LC_LIGHT_FOCUSED) != 0; }
bool IsTargetFocused()
{ return (m_nState & LC_LIGHT_TARGET_FOCUSED) != 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 GetTargetPos (float *pos) const
{ memcpy (pos, m_fTarget, sizeof (float[3])); }
LightTarget* GetTarget () const
{ return m_pTarget; }
const char* GetName() const
{ return m_strName; };
void Render (float fLineWidth);
void MinIntersectDist (LC_CLICKLINE* Line);
bool IntersectsVolume(const Vector4* Planes, int NumPlanes)
{ return false; }
void UpdatePosition (unsigned short nTime, bool bAnimation);
void Move (unsigned short nTime, bool bAnimation, bool bAddKey, float dx, float dy, float dz);
void Setup (int index);
void CreateName(const Light* pLight);
protected:
void Initialize ();
// Camera target
LightTarget* m_pTarget;
// Attributes
float m_fCone;
unsigned char m_nState;
char m_strName[81];
bool m_bEnabled;
GLuint m_nList;
static GLuint m_nSphereList;
static GLuint m_nTargetList;
// Temporary parameters
float m_fPos[4];
float m_fTarget[3];
float m_fAmbient[4];
float m_fDiffuse[4];
float m_fSpecular[4];
float m_fConstant;
float m_fLinear;
float m_fQuadratic;
float m_fCutoff;
float m_fExponent;
};
#endif // _LIGHT_H_

101
common/mainwnd.cpp Normal file
View file

@ -0,0 +1,101 @@
//
// Main LeoCAD window
//
#include <stdio.h>
#include "mainwnd.h"
#include "system.h"
MainWnd::MainWnd ()
: BaseWnd (NULL, LC_MAINWND_NUM_COMMANDS)
{
char entry[8];
int i;
for (i = 0; i < LC_MRU_MAX; i++)
{
sprintf (entry, "File%d", i+1);
m_strMRU[i] = Sys_ProfileLoadString ("RecentFiles", entry, "");
}
}
MainWnd::~MainWnd ()
{
char entry[8];
int i;
for (i = 0; i < LC_MRU_MAX; i++)
{
sprintf (entry, "File%d", i+1);
Sys_ProfileSaveString ("RecentFiles", entry, m_strMRU[i]);
}
}
// =============================================================================
// recently used files
void MainWnd::UpdateMRU ()
{
#ifdef LC_WINDOWS
// FIXME !!
void SystemUpdateRecentMenu(char names[4][LC_MAXPATH]);
char names[4][LC_MAXPATH];
for (int i = 0; i < LC_MRU_MAX; i++)
strcpy (names[i], m_strMRU[i]);
SystemUpdateRecentMenu(names);
#else
for (int i = 0; i < LC_MRU_MAX; i++)
{
if (m_strMRU[i].IsEmpty ())
{
if (i == 0)
{
SetMenuItemText (LC_MAINWND_RECENT1, "Recent Files");
EnableMenuItem (LC_MAINWND_RECENT1, false);
}
else
ShowMenuItem (LC_MAINWND_RECENT1+i, false);
}
else
{
char text[LC_MAXPATH+8];
sprintf (text, "&%d- %s", i+1, (char*)m_strMRU[i]);
ShowMenuItem (LC_MAINWND_RECENT1+i, true);
EnableMenuItem (LC_MAINWND_RECENT1+i, true);
SetMenuItemText (LC_MAINWND_RECENT1+i, text);
}
}
#endif
}
void MainWnd::AddToMRU(const char* Filename)
{
// Make a copy of the string in case we're loading a file from the MRU menu.
String str = Filename;
int i;
// Search for Filename in the MRU list.
for (i = 0; i < (LC_MRU_MAX - 1); i++)
if (m_strMRU[i] == Filename)
break;
// Move MRU strings before this one down.
for (; i > 0; i--)
m_strMRU[i] = m_strMRU[i-1];
m_strMRU[0] = str;
UpdateMRU();
}
void MainWnd::RemoveFromMRU(int index)
{
for (int i = index; i < (LC_MRU_MAX - 1); i++)
m_strMRU[i] = m_strMRU[i+1];
m_strMRU[LC_MRU_MAX - 1].Empty ();
UpdateMRU ();
}

34
common/mainwnd.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef _MAINWND_H_
#define _MAINWND_H_
#include "str.h"
#include "basewnd.h"
#define LC_MRU_MAX 4
typedef enum
{
LC_MAINWND_RECENT1,
LC_MAINWND_RECENT2,
LC_MAINWND_RECENT3,
LC_MAINWND_RECENT4,
LC_MAINWND_NUM_COMMANDS
} LC_MAINWND_COMMANDS;
class MainWnd : public BaseWnd
{
public:
MainWnd ();
virtual ~MainWnd ();
void UpdateMRU ();
void AddToMRU (const char *filename);
void RemoveFromMRU (int index);
const char* GetMRU (int index) const
{ return m_strMRU[index]; }
protected:
String m_strMRU[LC_MRU_MAX];
};
#endif // _MAINWND_H_

688
common/matrix.cpp Normal file
View file

@ -0,0 +1,688 @@
//
// 4x4 Matrix class
//
#include <memory.h>
#include <math.h>
#include <string.h>
#include "matrix.h"
#include "defines.h"
// =============================================================================
// static functions
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 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;
}
// =============================================================================
// Matrix class
Matrix::Matrix ()
{
LoadIdentity();
}
Matrix::Matrix (const float* mat)
{
memcpy (&m[0], mat, sizeof(float[16]));
}
// Create a matrix from axis-angle and a point
Matrix::Matrix (const float *rot, const float *pos)
{
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;
}
// Expand from the .bin file
void Matrix::FromPacked (const float *mat)
{
m[0] = mat[0];
m[1] = mat[1];
m[2] = mat[2];
m[3] = 0.0f;
m[4] = mat[3];
m[5] = mat[4];
m[6] = mat[5];
m[7] = 0.0f;
m[8] = mat[6];
m[9] = mat[7];
m[10] = mat[8];
m[11] = 0.0f;
m[12] = mat[9];
m[13] = mat[10];
m[14] = mat[11];
m[15] = 0.0f;
}
void Matrix::FromFloat (const float* mat)
{
memcpy (&m[0], mat, sizeof(float[16]));
}
void Matrix::LoadIdentity ()
{
memcpy (&m[0], &Identity, sizeof(float[16]));
}
float Matrix::Determinant() const
{
return m[0]*m[5]*m[10] + m[1]*m[6]*m[8] + m[2]*m[4]*m[9] - m[0]*m[6]*m[9] - m[1]*m[4]*m[10] - m[2]*m[5]*m[8];
}
void Matrix::Multiply(const Matrix& m1, const Matrix& m2)
{
matmul(m, m1.m, m2.m);
}
void Matrix::Rotate (float angle, float x, float y, float z)
{
float rm[16];
if (angle == 0.0)
return;
rotation_matrix(angle, x, y, z, rm);
matmul(rm, rm, m);
memcpy (&m[0], &rm[0], sizeof(rm));
}
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::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::FromLDraw (const float *f)
{
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[3] = 0.0f;
m[4] = f[4]; m[5] = f[7]; m[6] = f[10]; m[7] = 0.0f;
m[8] = f[5]; m[9] = f[8]; m[10]= f[11]; m[11] = 0.0f;
m[12]= f[0]/25; m[13]= f[1]/25; m[14]= f[2]/25; m[15] = 1.0f;
matmul (m, m, t);
matmul (trans, trans, m);
memcpy (&m[0], &trans[0], sizeof(m));
}
void Matrix::ToLDraw (float *f) const
{
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::ToEulerAngles (float *rot) const
{
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) const
{
Matrix tmp(*this);
// Normalize.
float inv;
inv = 1.0f / sqrtf(tmp.m[0]*tmp.m[0] + tmp.m[1]*tmp.m[1] + tmp.m[2]*tmp.m[2]);
tmp.m[0] *= inv; tmp.m[1] *= inv; tmp.m[2] *= inv;
inv = 1.0f / sqrtf(tmp.m[4]*tmp.m[4] + tmp.m[5]*tmp.m[5] + tmp.m[6]*tmp.m[6]);
tmp.m[4] *= inv; tmp.m[5] *= inv; tmp.m[6] *= inv;
inv = 1.0f / sqrtf(tmp.m[8]*tmp.m[8] + tmp.m[9]*tmp.m[9] + tmp.m[10]*tmp.m[10]);
tmp.m[8] *= inv; tmp.m[9] *= inv; tmp.m[10] *= inv;
// Determinant should be 1 for rotation matrices.
if (tmp.Determinant() < 0.0f)
{
tmp.m[0] *= -1.0f;
tmp.m[1] *= -1.0f;
tmp.m[2] *= -1.0f;
}
float fTrace = tmp.m[0] + tmp.m[5] + tmp.m[10];
float fCos = 0.5f * (fTrace - 1.0f);
rot[3] = acosf(fCos); // in [0,PI]
if (rot[3] > 0.01f)
{
if (fabs (M_PI - rot[3]) > 0.01f)
{
rot[0] = tmp.m[6] - tmp.m[9];
rot[1] = tmp.m[8] - tmp.m[2];
rot[2] = tmp.m[1] - tmp.m[4];
inv = 1.0f / sqrtf(rot[0]*rot[0] + rot[1]*rot[1] + rot[2]*rot[2]);
rot[0] *= inv;
rot[1] *= inv;
rot[2] *= inv;
}
else
{
// angle is PI
float fHalfInverse;
if (tmp.m[0] >= tmp.m[5])
{
// r00 >= r11
if (tmp.m[0] >= tmp.m[10])
{
// r00 is maximum diagonal term
rot[0] = 0.5f * sqrtf(tmp.m[0] - tmp.m[5] - tmp.m[10] + 1.0f);
fHalfInverse = 0.5f / rot[0];
rot[1] = fHalfInverse * tmp.m[4];
rot[2] = fHalfInverse * tmp.m[8];
}
else
{
// r22 is maximum diagonal term
rot[2] = 0.5f * sqrtf(tmp.m[10] - tmp.m[0] - tmp.m[5] + 1.0f);
fHalfInverse = 0.5f / rot[2];
rot[0] = fHalfInverse * tmp.m[8];
rot[1] = fHalfInverse * tmp.m[9];
}
}
else
{
// r11 > r00
if (tmp.m[5] >= tmp.m[10])
{
// r11 is maximum diagonal term
rot[1] = 0.5f * sqrtf(tmp.m[5] - tmp.m[0] - tmp.m[10] + 1.0f);
fHalfInverse = 0.5f / rot[1];
rot[0] = fHalfInverse * tmp.m[4];
rot[2] = fHalfInverse * tmp.m[9];
}
else
{
// r22 is maximum diagonal term
rot[2] = 0.5f * sqrtf(tmp.m[10] - tmp.m[0] - tmp.m[5] + 1.0f);
fHalfInverse = 0.5f / rot[2];
rot[0] = fHalfInverse * tmp.m[8];
rot[1] = fHalfInverse * tmp.m[9];
}
}
}
}
else
{
// The angle is 0 and the matrix is the identity. Any axis will
// work, so just use the z-axis.
rot[0] = 0.0f;
rot[1] = 0.0f;
rot[2] = 1.0f;
}
rot[3] *= RTOD;
}
void Matrix::FromEulerAngles (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 (const float *axis, float angle)
{
if (angle == 0.0f)
return;
rotation_matrix (angle, axis[0], axis[1], axis[2], m);
}
void Matrix::Transpose3()
{
float tmp;
tmp = m[1]; m[1] = m[4]; m[4] = tmp;
tmp = m[2]; m[2] = m[8]; m[8] = tmp;
tmp = m[6]; m[6] = m[9]; m[9] = tmp;
}
bool Matrix::Invert ()
{
double t, inverse[16];
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] = m[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;
}
}
}
}
for (i = 0; i < 16; i++)
m[i] = (float)inverse[i];
return true;
}
void Matrix::CreatePerspective (float fovy, float aspect, float nearval, float farval)
{
float left, right, bottom, top;
float x, y, a, b, c, d;
LoadIdentity ();
top = nearval * (float)tan (fovy * M_PI / 360.0);
bottom = -top;
left = bottom * aspect;
right = top * aspect;
if ((nearval<=0.0 || farval<=0.0) || (nearval == farval) || (left == right) || (top == bottom))
return;
x = (2.0f*nearval) / (right-left);
y = (2.0f*nearval) / (top-bottom);
a = (right+left) / (right-left);
b = (top+bottom) / (top-bottom);
c = -(farval+nearval) / ( farval-nearval);
d = -(2.0f*farval*nearval) / (farval-nearval);
#define M(row,col) m[col*4+row]
M(0,0) = x; M(0,1) = 0.0F; M(0,2) = a; M(0,3) = 0.0F;
M(1,0) = 0.0F; M(1,1) = y; M(1,2) = b; M(1,3) = 0.0F;
M(2,0) = 0.0F; M(2,1) = 0.0F; M(2,2) = c; M(2,3) = d;
M(3,0) = 0.0F; M(3,1) = 0.0F; M(3,2) = -1.0F; M(3,3) = 0.0F;
#undef M
}
void Matrix::CreateLookat (const float *eye, const float *target, const float *up)
{
float x[3], y[3], z[3];
float mag;
z[0] = eye[0] - target[0];
z[1] = eye[1] - target[1];
z[2] = eye[2] - target[2];
mag = (float)sqrt (z[0]*z[0] + z[1]*z[1] + z[2]*z[2]);
if (mag)
{
z[0] /= mag;
z[1] /= mag;
z[2] /= mag;
}
y[0] = up[0];
y[1] = up[1];
y[2] = up[2];
// X vector = Y cross Z
x[0] = y[1]*z[2] - y[2]*z[1];
x[1] = -y[0]*z[2] + y[2]*z[0];
x[2] = y[0]*z[1] - y[1]*z[0];
// Recompute Y = Z cross X
y[0] = z[1]*x[2] - z[2]*x[1];
y[1] = -z[0]*x[2] + z[2]*x[0];
y[2] = z[0]*x[1] - z[1]*x[0];
mag = (float)sqrt (x[0]*x[0] + x[1]*x[1] + x[2]*x[2]);
if (mag)
{
x[0] /= mag;
x[1] /= mag;
x[2] /= mag;
}
mag = (float)sqrt (y[0]*y[0] + y[1]*y[1] + y[2]*y[2]);
if (mag)
{
y[0] /= mag;
y[1] /= mag;
y[2] /= mag;
}
#define M(row,col) m[col*4+row]
M(0,0) = x[0]; M(0,1) = x[1]; M(0,2) = x[2]; M(0,3) = 0.0;
M(1,0) = y[0]; M(1,1) = y[1]; M(1,2) = y[2]; M(1,3) = 0.0;
M(2,0) = z[0]; M(2,1) = z[1]; M(2,2) = z[2]; 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
// Translate Eye to Origin
m[12] = m[0] * -eye[0] + m[4] * -eye[1] + m[8] * -eye[2] + m[12];
m[13] = m[1] * -eye[0] + m[5] * -eye[1] + m[9] * -eye[2] + m[13];
m[14] = m[2] * -eye[0] + m[6] * -eye[1] + m[10] * -eye[2] + m[14];
m[15] = m[3] * -eye[0] + m[7] * -eye[1] + m[11] * -eye[2] + m[15];
}

49
common/matrix.h Normal file
View file

@ -0,0 +1,49 @@
// Matrix class
//
#ifndef _MATRIX_H_
#define _MATRIX_H_
class Matrix
{
public:
Matrix();
Matrix(const float *mat);
Matrix(const float *rot, const float *pos);
~Matrix() { };
void FromPacked(const float *mat);
void FromFloat(const float* mat);
void FromLDraw(const float *f);
void FromEulerAngles(float yaw, float pitch, float roll);
void FromAxisAngle(const float *axis, float angle);
void ToLDraw(float *f) const;
void ToEulerAngles(float *rot) const;
void ToAxisAngle(float *rot) const;
void LoadIdentity();
void Translate(float x, float y, float z);
void Multiply(const Matrix& m1, const Matrix& m2);
bool Invert();
void Transpose3();
float Determinant() const;
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 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 CreatePerspective(float fovy, float aspect, float nearval, float farval);
void CreateLookat(const float *eye, const float *target, const float *up);
public:
float m[16];
};
#endif //_MATRIX_H_

32
common/message.cpp Normal file
View file

@ -0,0 +1,32 @@
//
// LeoCAD messaging system
//
#include "message.h"
Messenger::Messenger ()
{
m_nRef = 0;
}
Messenger::~Messenger ()
{
for (int i = 0; i < m_Listeners.GetSize (); i++)
delete m_Listeners[i];
}
void Messenger::Dispatch (int message, void *data)
{
for (int i = 0; i < m_Listeners.GetSize (); i++)
m_Listeners[i]->func (message, data, m_Listeners[i]->user);
}
void Messenger::Listen (LC_MSG_CALLBACK func, void *user)
{
LC_MSG_STRUCT *s = new LC_MSG_STRUCT;
s->func = func;
s->user = user;
m_Listeners.Add (s);
}

40
common/message.h Normal file
View file

@ -0,0 +1,40 @@
#ifndef _MESSAGE_H_
#define _MESSAGE_H_
#include "array.h"
typedef void (*LC_MSG_CALLBACK) (int message, void *data, void *user);
typedef enum
{
LC_MSG_FOCUS_CHANGED,
// LC_MSG_SELECTION_CHANGED,
LC_MSG_COUNT
} LC_MSG_TYPES;
typedef struct
{
LC_MSG_CALLBACK func;
void *user;
} LC_MSG_STRUCT;
class Messenger
{
public:
Messenger ();
~Messenger ();
void AddRef ()
{ m_nRef++; };
void DecRef ()
{ m_nRef--; if (m_nRef == 0) delete this; };
void Dispatch (int message, void *data);
void Listen (LC_MSG_CALLBACK func, void *user);
protected:
int m_nRef;
PtrArray<LC_MSG_STRUCT> m_Listeners;
};
#endif // _MESSAGE_H_

1369
common/minifig.cpp Normal file

File diff suppressed because it is too large Load diff

73
common/minifig.h Normal file
View file

@ -0,0 +1,73 @@
#ifndef _MINIFIG_H_
#define _MINIFIG_H_
#include "glwindow.h"
#include "file.h"
#include "algebra.h"
#include "array.h"
class PieceInfo;
enum LC_MFW_TYPES
{
LC_MFW_HAT,
LC_MFW_HEAD,
LC_MFW_TORSO,
LC_MFW_NECK,
LC_MFW_LEFT_ARM,
LC_MFW_RIGHT_ARM,
LC_MFW_LEFT_HAND,
LC_MFW_RIGHT_HAND,
LC_MFW_LEFT_TOOL,
LC_MFW_RIGHT_TOOL,
LC_MFW_HIPS,
LC_MFW_LEFT_LEG,
LC_MFW_RIGHT_LEG,
LC_MFW_LEFT_SHOE,
LC_MFW_RIGHT_SHOE,
LC_MFW_NUMITEMS
};
struct lcMinifigPieceInfo
{
char Description[80];
PieceInfo* Info;
Matrix44 Offset;
};
class MinifigWizard : public GLWindow
{
public:
MinifigWizard (GLWindow *share);
~MinifigWizard ();
void OnDraw ();
void Calculate();
int GetSelectionIndex(int Type) const;
void SetSelectionIndex(int Type, int Index);
void SetColor(int Type, int Color);
void SetAngle(int Type, float Angle);
void GetMinifigNames (char ***names, int *count);
void SaveMinifig (const char* name);
bool LoadMinifig (const char* name);
void DeleteMinifig (const char* name);
void ParseSettings(File& Settings);
ObjArray<lcMinifigPieceInfo> mSettings[LC_MFW_NUMITEMS];
PieceInfo* m_Info[LC_MFW_NUMITEMS];
int m_Colors[LC_MFW_NUMITEMS];
float m_Angles[LC_MFW_NUMITEMS];
Matrix44 m_Matrices[LC_MFW_NUMITEMS];
protected:
// saved minifig templates
int m_MinifigCount;
char **m_MinifigNames;
char **m_MinifigTemplates;
};
#endif // _MINIFIG_H_

22
common/module.mk Normal file
View file

@ -0,0 +1,22 @@
SRC += common/algebra.cpp common/camera.cpp common/console.cpp common/curve.cpp common/file.cpp \
common/globals.cpp common/group.cpp common/image.cpp common/im_bmp.cpp common/im_gif.cpp \
common/lc_application.cpp common/library.cpp common/light.cpp common/mainwnd.cpp \
common/matrix.cpp common/message.cpp common/minifig.cpp common/object.cpp common/opengl.cpp \
common/piece.cpp common/pieceinf.cpp common/preview.cpp common/project.cpp common/quant.cpp \
common/str.cpp common/terrain.cpp common/texfont.cpp common/texture.cpp common/tr.cpp \
common/vector.cpp common/view.cpp
ifeq ($(HAVE_JPEGLIB), yes)
LIBS += -ljpeg
SRC += common/im_jpg.cpp
endif
ifeq ($(HAVE_ZLIB), yes)
ifeq ($(HAVE_PNGLIB), yes)
LIBS += -lpng -lz
SRC += common/im_png.cpp
endif
endif
LIBS += -lm

568
common/object.cpp Executable file
View file

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

213
common/object.h Executable file
View file

@ -0,0 +1,213 @@
#ifndef _OBJECT_H_
#define _OBJECT_H_
class File;
class Matrix;
class Object;
/*
#define LC_OBJECT_NAME_LEN 80
#define LC_OBJECT_HIDDEN 0x01
#define LC_OBJECT_SELECTED 0x02
#define LC_OBJECT_FOCUSED 0x04
*/
typedef enum
{
LC_OBJECT_PIECE,
LC_OBJECT_CAMERA,
LC_OBJECT_CAMERA_TARGET,
LC_OBJECT_LIGHT,
LC_OBJECT_LIGHT_TARGET,
LC_OBJECT_CURVE,
LC_OBJECT_CURVE_POINT,
// LC_OBJECT_GROUP,
// LC_OBJECT_GROUP_PIVOT,
} LC_OBJECT_TYPE;
// key handling
typedef struct LC_OBJECT_KEY
{
unsigned short time;
float param[4];
unsigned char type;
LC_OBJECT_KEY* next;
} LC_OBJECT_KEY;
typedef struct
{
const char *description;
unsigned char size; // number of floats
unsigned char type;
} LC_OBJECT_KEY_INFO;
// rendering parameters
typedef struct
{
bool lighting;
bool edges;
float fLineWidth;
unsigned char lastcolor;
bool transparent;
} LC_RENDER_INFO;
// Callback "closure" struct, used to make the necessary parameters known to
// the callback function.
typedef struct LC_CLICKLINE
{
float a1, b1, c1;
float a2, b2, c2;
float mindist;
Object *pClosest;
double PointDistance (float *point);
} LC_CLICKLINE;
class Object
{
public:
Object (LC_OBJECT_TYPE nType);
virtual ~Object ();
public:
// Move the object.
virtual void Move(unsigned short nTime, bool bAnimation, bool bAddKey, float dx, float dy, float dz) = 0;
// Check if the object intersects the ray.
virtual void MinIntersectDist(LC_CLICKLINE* pLine) = 0;
// bSelecting is the action (add/remove), bFocus means "add focus if selecting"
// or "remove focus only if deselecting", bMultiple = Ctrl key is down
virtual void Select(bool bSelecting, bool bFocus, bool bMultiple) = 0;
// Check if the object intersects the volume specified by a given set of planes.
virtual bool IntersectsVolume(const class Vector4* Planes, int NumPlanes) = 0;
/*
virtual void UpdatePosition (unsigned short nTime, bool bAnimation) = 0;
virtual void CompareBoundingBox (float *box) { };
virtual void Render (LC_RENDER_INFO* pInfo) = 0;
// Query functions
virtual bool IsSelected () const
{ return (m_nState & LC_OBJECT_SELECTED) != 0; };
virtual bool IsFocused () const
{ return (m_nState & LC_OBJECT_FOCUSED) != 0; };
virtual bool IsVisible (unsigned short nTime, bool bAnimation) const
{ return (m_nState & LC_OBJECT_HIDDEN) == 0; }
const char* GetName() const
{ return m_strName; }
// State change, most classes will have to replace these functions
virtual void SetSelection (bool bSelect, void *pParam = NULL)
{
if (bSelect)
m_nState |= LC_OBJECT_SELECTED;
else
m_nState &= ~(LC_OBJECT_SELECTED | LC_OBJECT_FOCUSED);
};
virtual void SetFocus (bool bFocus, void *pParam = NULL)
{
if (bFocus)
m_nState |= (LC_OBJECT_SELECTED | LC_OBJECT_FOCUSED);
else
m_nState &= ~LC_OBJECT_FOCUSED;
};
virtual void SetVisible (bool bVisible)
{
if (bVisible)
m_nState &= ~LC_OBJECT_HIDDEN;
else
{
m_nState |= LC_OBJECT_HIDDEN;
SetSelection (false, NULL);
}
}
virtual bool SetColor (int nColor)
{ return false; };
*/
// determine the object type
bool IsPiece () const
{ return m_nObjectType == LC_OBJECT_PIECE; }
bool IsCamera () const
{ return m_nObjectType == LC_OBJECT_CAMERA; }
bool IsLight () const
{ return m_nObjectType == LC_OBJECT_LIGHT; }
bool IsCurve () const
{ return m_nObjectType == LC_OBJECT_CURVE; }
LC_OBJECT_TYPE GetType () const
{ return m_nObjectType; }
virtual const char* GetName() const = 0;
/*
// For linked lists
Object* m_pNext;
Object* m_pNextRender;
Object* m_pParent;
Object* GetTopAncestor () const
{ return m_pParent ? m_pParent->GetTopAncestor () : this; }
*/
protected:
// Str m_strName;
// unsigned char m_nState;
virtual bool FileLoad (File& file);
virtual void FileSave (File& file) const;
// Key handling stuff
public:
void CalculateSingleKey (unsigned short nTime, bool bAnimation, int keytype, float *value) const;
void ChangeKey (unsigned short time, bool animation, bool addkey, const float *param, unsigned char keytype);
virtual void InsertTime (unsigned short start, bool animation, unsigned short time);
virtual void RemoveTime (unsigned short start, bool animation, unsigned short time);
int GetKeyTypeCount () const
{ return m_nKeyInfoCount; }
const LC_OBJECT_KEY_INFO* GetKeyTypeInfo (int index) const
{ return &m_pKeyInfo[index]; };
const float* GetKeyTypeValue (int index) const
{ return m_pKeyValues[index]; };
protected:
void RegisterKeys (float *values[], LC_OBJECT_KEY_INFO* info, int count);
void CalculateKeys (unsigned short nTime, bool bAnimation);
private:
void RemoveKeys ();
LC_OBJECT_KEY* m_pAnimationKeys;
LC_OBJECT_KEY* m_pInstructionKeys;
float **m_pKeyValues;
LC_OBJECT_KEY_INFO *m_pKeyInfo;
int m_nKeyInfoCount;
// Bounding box stuff
protected:
double BoundingBoxIntersectDist (LC_CLICKLINE* pLine) const;
void BoundingBoxCalculate (float pos[3]);
void BoundingBoxCalculate (Matrix *mat);
void BoundingBoxCalculate (Matrix *mat, float Dimensions[6]);
private:
bool BoundingBoxIntersectionbyLine (double a1, double b1, double c1, double a2, double b2, double c2,
double *x, double *y, double *z) const;
bool BoundingBoxPointInside (double x, double y, double z) const;
float m_fBoxPlanes[4][6];
// Object type
private:
LC_OBJECT_TYPE m_nObjectType;
};
#endif

1549
common/opengl.cpp Executable file

File diff suppressed because it is too large Load diff

1274
common/opengl.h Executable file

File diff suppressed because it is too large Load diff

1883
common/piece.cpp Normal file

File diff suppressed because it is too large Load diff

155
common/piece.h Normal file
View file

@ -0,0 +1,155 @@
#ifndef _PIECE_H_
#define _PIECE_H_
class File;
class Piece;
class Group;
class PieceInfo;
#include "object.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
{
LC_PK_POSITION,
LC_PK_ROTATION,
LC_PK_COUNT
} LC_PK_TYPES;
class Piece : public Object
{
public:
Piece (PieceInfo* pPieceInfo);
~Piece ();
void Select (bool bSelecting, bool bFocus, bool bMultiple);
virtual void InsertTime (unsigned short start, bool animation, unsigned short time);
virtual void RemoveTime (unsigned short start, bool animation, unsigned short time);
virtual bool IntersectsVolume(const Vector4* Planes, int NumPlanes);
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; }
bool IsSelected()
{ return (m_nState & LC_PIECE_SELECTED) != 0; }
bool IsFocused()
{ return (m_nState & LC_PIECE_FOCUSED) != 0; }
const char* GetName() const
{ return m_strName; };
void MinIntersectDist(LC_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);
bool 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);
void Move(unsigned short nTime, bool bAnimation, bool bAddKey, float dx, float dy, float dz);
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; }
inline Vector3 GetPosition() const
{ return Vector3(m_fPosition[0], m_fPosition[1], m_fPosition[2]); }
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 bEdges, unsigned char* nLastColor, bool* bTrans);
void RenderBox(bool bHilite, float fLineWidth);
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 BuildDrawInfo();
// Atributes
PieceInfo* m_pPieceInfo;
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];
CONNECTION* m_pConnections;
void* m_pDrawInfo;
};
#endif // _PIECE_H

1946
common/pieceinf.cpp Normal file

File diff suppressed because it is too large Load diff

130
common/pieceinf.h Normal file
View file

@ -0,0 +1,130 @@
//
// pieceinf.h
////////////////////////////////////////////////////
#ifndef _PIECEINF_H_
#define _PIECEINF_H_
#include <stdio.h>
#ifndef GLuint
#include "opengl.h"
#endif
#include "algebra.h"
#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)
#define LC_PIECE_LONGDATA_RUNTIME 0x40 // If the original data is 16 bits but we expanded to 32 bits
#define LC_PIECE_NAME_LEN 256
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 ();
bool IsPatterned() const
{
const char* Name = m_strName;
while (*Name)
{
if (*Name < '0' || *Name > '9')
break;
Name++;
}
if (*Name == 'P')
return true;
return false;
}
bool IsSubPiece() const
{
return (m_strDescription[0] == '~');
}
Vector3 GetCenter() const
{
return Vector3((m_fDimensions[0] + m_fDimensions[3]) * 0.5f,
(m_fDimensions[1] + m_fDimensions[4]) * 0.5f,
(m_fDimensions[2] + m_fDimensions[5]) * 0.5f);
}
// Operations
void ZoomExtents(float Fov, float Aspect, float* EyePos = NULL) const;
void RenderOnce(int nColor);
void RenderPiece(int nColor);
void WriteWavefront(FILE* file, unsigned char color, unsigned long* start);
// Implementation
GLuint GetBoxDisplayList()
{
if (!m_nBoxList)
CreateBoxDisplayList();
return m_nBoxList;
};
void LoadIndex(File& file);
void AddRef();
void DeRef();
public:
// Attributes
char m_strName[LC_PIECE_NAME_LEN];
char m_strDescription[65];
float m_fDimensions[6];
unsigned long m_nOffset;
unsigned long m_nSize;
// 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();
void CreateBoxDisplayList();
};
#endif // _PIECEINF_H_

185
common/preview.cpp Normal file
View file

@ -0,0 +1,185 @@
//
// Piece Preview window
//
#include "preview.h"
#include "globals.h"
#include "project.h"
#include "pieceinf.h"
#include "system.h"
#include "lc_application.h"
PiecePreview::PiecePreview(GLWindow *share)
: GLWindow(share)
{
m_PieceInfo = NULL;
m_RotateX = 60.0f;
m_RotateZ = 225.0f;
m_Distance = 10.0f;
m_AutoZoom = true;
m_Tracking = LC_TRACK_NONE;
}
PiecePreview::~PiecePreview()
{
}
void PiecePreview::OnDraw()
{
if (m_PieceInfo == NULL)
return;
if (!MakeCurrent())
return;
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.5f, 0.1f);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glDisable(GL_DITHER);
glShadeModel(GL_FLAT);
float aspect = (float)m_nWidth/(float)m_nHeight;
glViewport(0, 0, m_nWidth, m_nHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(30.0f, aspect, 1.0f, 100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
Vector3 Eye(0, 0, 1.0f);
Matrix33 Rot;
Rot.CreateFromAxisAngle(Vector3(1, 0, 0), -m_RotateX * LC_DTOR);
Eye = Mul(Eye, Rot);
Rot.CreateFromAxisAngle(Vector3(0, 0, 1), -m_RotateZ * LC_DTOR);
Eye = Mul(Eye, Rot);
if (m_AutoZoom)
{
Eye = Eye * 100.0f;
m_PieceInfo->ZoomExtents(30.0f, aspect, Eye);
// Update the new camera distance.
Vector3 d = Eye - m_PieceInfo->GetCenter();
m_Distance = d.Length();
}
else
{
Matrix44 WorldToView;
WorldToView.CreateLookAt(Eye * m_Distance, m_PieceInfo->GetCenter(), Vector3(0, 0, 1));
glLoadMatrixf(WorldToView);
}
float pos[4] = { 0, 0, 10, 0 }, *bg = lcGetActiveProject()->GetBackgroundColor ();
glLightfv(GL_LIGHT0, GL_POSITION, pos);
glClearColor(bg[0], bg[1], bg[2], bg[3]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_PieceInfo->RenderPiece(lcGetActiveProject()->GetCurrentColor());
glFinish();
SwapBuffers();
}
void PiecePreview::SetCurrentPiece(PieceInfo *pInfo)
{
MakeCurrent();
if (m_PieceInfo != NULL)
m_PieceInfo->DeRef();
m_PieceInfo = pInfo;
if (m_PieceInfo != NULL)
{
m_PieceInfo->AddRef();
lcGetActiveProject()->SetCurrentPiece(m_PieceInfo);
Redraw();
}
}
void PiecePreview::OnLeftButtonDown(int x, int y, bool Control, bool Shift)
{
if (m_Tracking == LC_TRACK_NONE)
{
m_DownX = x;
m_DownY = y;
m_Tracking = LC_TRACK_LEFT;
CaptureMouse();
}
}
void PiecePreview::OnLeftButtonUp(int x, int y, bool Control, bool Shift)
{
if (m_Tracking == LC_TRACK_LEFT)
{
m_Tracking = LC_TRACK_NONE;
ReleaseMouse();
}
}
void PiecePreview::OnLeftButtonDoubleClick(int x, int y, bool Control, bool Shift)
{
m_AutoZoom = true;
Redraw();
}
void PiecePreview::OnRightButtonDown(int x, int y, bool Control, bool Shift)
{
if (m_Tracking == LC_TRACK_NONE)
{
m_DownX = x;
m_DownY = y;
m_Tracking = LC_TRACK_RIGHT;
CaptureMouse();
}
}
void PiecePreview::OnRightButtonUp(int x, int y, bool Control, bool Shift)
{
if (m_Tracking == LC_TRACK_RIGHT)
{
m_Tracking = LC_TRACK_NONE;
ReleaseMouse();
}
}
void PiecePreview::OnMouseMove(int x, int y, bool Control, bool Shift)
{
if (m_Tracking == LC_TRACK_LEFT)
{
// Rotate.
m_RotateZ += x - m_DownX;
m_RotateX += y - m_DownY;
if (m_RotateX > 179.5f)
m_RotateX = 179.5f;
else if (m_RotateX < 0.5f)
m_RotateX = 0.5f;
m_DownX = x;
m_DownY = y;
Redraw();
}
else if (m_Tracking == LC_TRACK_RIGHT)
{
// Zoom.
m_Distance += (float)(y - m_DownY) * 0.2f;
m_AutoZoom = false;
if (m_Distance < 0.5f)
m_Distance = 0.5f;
m_DownX = x;
m_DownY = y;
Redraw();
}
}

42
common/preview.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef _PREVIEW_H_
#define _PREVIEW_H_
#include "glwindow.h"
class PieceInfo;
class PiecePreview : public GLWindow
{
public:
PiecePreview(GLWindow *share);
virtual ~PiecePreview();
void OnDraw();
void OnLeftButtonDown(int x, int y, bool Control, bool Shift);
void OnLeftButtonUp(int x, int y, bool Control, bool Shift);
void OnLeftButtonDoubleClick(int x, int y, bool Control, bool Shift);
void OnRightButtonDown(int x, int y, bool Control, bool Shift);
void OnRightButtonUp(int x, int y, bool Control, bool Shift);
void OnMouseMove(int x, int y, bool Control, bool Shift);
PieceInfo* GetCurrentPiece() const
{ return m_PieceInfo; }
void SetCurrentPiece(PieceInfo* Info);
protected:
PieceInfo* m_PieceInfo;
// Mouse tracking.
int m_Tracking;
int m_DownX;
int m_DownY;
// Current camera settings.
float m_Distance;
float m_RotateX;
float m_RotateZ;
bool m_AutoZoom;
};
#endif // _PREVIEW_H_

9338
common/project.cpp Normal file

File diff suppressed because it is too large Load diff

313
common/project.h Normal file
View file

@ -0,0 +1,313 @@
#ifndef _PROJECT_H_
#define _PROJECT_H_
#include "object.h"
#include "defines.h"
#include "typedefs.h"
#include "opengl.h"
#include "array.h"
#include "algebra.h"
typedef enum
{
LC_TRACK_NONE, LC_TRACK_START_LEFT, LC_TRACK_LEFT,
LC_TRACK_START_RIGHT, LC_TRACK_RIGHT
} LC_MOUSE_TRACK;
// Mouse control overlays.
typedef enum
{
LC_OVERLAY_XYZ,
LC_OVERLAY_X,
LC_OVERLAY_Y,
LC_OVERLAY_Z,
LC_OVERLAY_XY,
LC_OVERLAY_XZ,
LC_OVERLAY_YZ
} LC_OVERLAY_MODES;
class Piece;
class Camera;
class Light;
class Group;
class Texture;
class Terrain;
class PieceInfo;
class Matrix;
class View;
class Image;
class PiecesLibrary;
class TexFont;
// Undo support
#include "file.h"
typedef struct LC_UNDOINFO
{
FileMem file;
char strText[21];
LC_UNDOINFO* pNext;
LC_UNDOINFO() { pNext = NULL; };
} LC_UNDOINFO;
class Project
{
public:
// Constructors
Project();
~Project();
// 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; }
void SetAnimation(bool Anim)
{ m_bAnimation = Anim; } // only to be called from lcApplication::Initialize()
unsigned short GetCurrentTime ()
{ return m_bAnimation ? m_nCurFrame : m_nCurStep; }
void SetCurrentPiece(PieceInfo* pInfo)
{ m_pCurPiece = pInfo; }
int GetCurrentColor () const
{ return m_nCurColor; }
float* GetBackgroundColor()
{ return m_fBackground; }
unsigned char GetAction() const
{ return m_nCurAction; }
int GetOverlayMode() const
{ return m_OverlayMode; }
void GetSnapIndex(int* SnapXY, int* SnapZ) const;
void GetSnapDistance(float* SnapXY, float* SnapZ) const;
void GetSnapDistanceText(char* SnapXY, char* SnapZ) const;
Camera* GetCamera(int i);
void GetTimeRange(int* from, int* to)
{
*from = m_bAnimation ? m_nCurFrame : m_nCurStep;
*to = m_bAnimation ? m_nTotalFrames : 255;
}
unsigned short GetTotalFrames () const
{ return m_nTotalFrames; }
void ConvertToUserUnits(Vector3& Value) const;
void ConvertFromUserUnits(Vector3& Value) const;
void GetArrays(Piece** ppPiece, Camera** ppCamera, Light** ppLight)
{
*ppPiece = m_pPieces;
*ppCamera = m_pCameras;
*ppLight = m_pLights;
}
void UpdateInterface();
void SetPathName (const char* lpszPathName, bool bAddToMRU);
void SetTitle (const char* lpszTitle);
public:
// Special notifications
void DeleteContents(bool bUndo); // delete doc items etc
void LoadDefaults(bool cameras);
void BeginPieceDrop(PieceInfo* Info);
void CreateImages(Image* images, int width, int height, unsigned short from, unsigned short to, bool hilite);
void Render(bool bToMemory);
void SetViewSize(int cx, int cy);
void CheckAutoSave();
bool GetSelectionCenter(Vector3& Center) const;
bool GetFocusPosition(Vector3& Position) const;
Object* GetFocusObject() const;
Group* AddGroup (const char* name, Group* pParent, float x, float y, float z);
void AddView (View* pView);
void RemoveView (View* pView);
void UpdateAllViews (View* pSender = NULL);
// Implementation
protected:
// default implementation
char m_strTitle[LC_MAXPATH];
char m_strPathName[LC_MAXPATH];
bool m_bModified; // changed since last saved
PtrArray<View> m_ViewList;
char m_strAuthor[101];
char m_strDescription[101];
char m_strComments[256];
// Piece library
TexFont* m_pScreenFont;
// Undo support
LC_UNDOINFO* m_pUndoList;
LC_UNDOINFO* m_pRedoList;
bool m_bUndoOriginal;
void CheckPoint (const 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();
void GetPieceInsertPosition(Piece* OffsetPiece, Vector3& Position, Vector4& Rotation);
void GetPieceInsertPosition(int MouseX, int MouseY, Vector3& Position, Vector4& Orientation);
void FindObjectFromPoint(int x, int y, LC_CLICKLINE* pLine, bool PiecesOnly = false);
void FindObjectsInBox(float x1, float y1, float x2, float y2, PtrArray<Object>& Objects);
void SelectAndFocusNone(bool bFocusOnly);
void GetActiveViewportMatrices(Matrix44& ModelView, Matrix44& Projection, int Viewport[4]);
void CalculateStep();
// Movement.
bool MoveSelectedObjects(Vector3& Move, Vector3& Remainder, bool Snap);
bool RotateSelectedObjects(Vector3& Delta, Vector3& Remainder);
void SnapVector(Vector3& Delta) const
{
Vector3 Dummy;
SnapVector(Delta, Dummy);
}
void SnapVector(Vector3& Delta, Vector3& Leftover) const;
void SnapRotationVector(Vector3& Delta, Vector3& Leftover) const;
// Rendering
void RenderScene(bool bShaded, bool bDrawViewports);
void RenderViewports(bool bBackground, bool bLines);
void RenderOverlays(int Viewport);
void RenderBoxes(bool bHilite);
void RenderInitialize();
void CreateHTMLPieceList(FILE* f, int nStep, bool bImages, const 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;
Vector3 m_MouseSnapLeftover;
Vector3 m_MouseTotalDelta;
int m_OverlayMode;
bool m_OverlayActive;
float m_OverlayScale[4];
Vector3 m_OverlayCenter;
Vector3 m_OverlayTrackStart;
Vector3 m_OverlayDelta;
void MouseUpdateOverlays(int x, int y);
void ActivateOverlay();
void UpdateOverlayScale();
void LoadViewportProjection(int Viewport);
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, bool bControl, bool bShift);
void OnLeftButtonUp(int x, int y, bool bControl, bool bShift);
void OnLeftButtonDoubleClick(int x, int y, bool bControl, bool bShift);
void OnRightButtonDown(int x, int y, bool bControl, bool bShift);
void OnRightButtonUp(int x, int y, bool bControl, bool bShift);
void OnMouseMove(int x, int y, bool bControl, bool bShift);
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);
void HandleMessage(int Message, void* Data);
protected:
// State variables
unsigned char m_nViewportMode;
unsigned char m_nActiveViewport;
int m_nViewX;
int m_nViewY;
PieceInfo* m_pCurPiece;
PieceInfo* m_PreviousPiece;
unsigned char m_nCurColor;
unsigned char m_nCurAction;
unsigned char m_PreviousAction;
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:
// File load/save implementation.
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, PtrArray<File>& FileArray);
void FileReadMPD(File& MPD, PtrArray<File>& FileArray) const;
public:
// File helpers
bool OnNewDocument();
bool OnOpenDocument(const char* FileName);
bool OpenProject(const char* FileName);
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_

640
common/quant.cpp Normal file
View file

@ -0,0 +1,640 @@
///////////////////////////////////////
// DL1 Quantization
#include <stdlib.h>
#include <math.h>
#include <memory.h>
#include <string.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 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;
}

31
common/quant.h Normal file
View file

@ -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_

402
common/str.cpp Normal file
View file

@ -0,0 +1,402 @@
//
// General purpose string class
//
#include <ctype.h>
#include "str.h"
static String aux;
// =============================================================================
// Construction / Destruction
String::String ()
{
m_pData = new char[1];
m_pData[0] = '\0';
}
String::~String ()
{
delete []m_pData;
}
// =============================================================================
// Operators
const String& String::operator= (const String& src)
{
delete []m_pData;
m_pData = new char[src.GetLength () + 1];
strcpy (m_pData, src.m_pData);
return *this;
}
const String& String::operator= (char ch)
{
delete []m_pData;
m_pData = new char[2];
m_pData[0] = ch;
m_pData[1] = '\0';
return *this;
}
const String& String::operator= (const char *src)
{
delete []m_pData;
m_pData = new char[strlen (src) + 1];
strcpy (m_pData, src);
return *this;
}
const String& String::operator+= (const String& src)
{
char *tmp = new char[GetLength () + src.GetLength () + 1];
strcpy (tmp, m_pData);
strcat (tmp, src.m_pData);
delete []m_pData;
m_pData = tmp;
return *this;
}
const String& String::operator+= (char ch)
{
int len = GetLength ();
char *tmp = new char[len + 1 + 1];
strcpy (tmp, m_pData);
tmp[len] = ch;
tmp[len+1] = '\0';
delete []m_pData;
m_pData = tmp;
return *this;
}
const String& String::operator+= (const char *src)
{
char *tmp = new char[GetLength () + strlen (src) + 1];
strcpy (tmp, m_pData);
strcat (tmp, src);
delete []m_pData;
m_pData = tmp;
return *this;
}
// =============================================================================
// Non-member operators
String& operator+ (const String& string1, const String& string2)
{
String s;
s = string1;
s += string2;
aux = s;
return aux;
}
String& operator+ (const String& string, char ch)
{
String s;
s = string;
s += ch;
aux = s;
return aux;
}
String& operator+ (char ch, const String& string)
{
String s;
s = ch;
s += string;
aux = s;
return aux;
}
String& operator+ (const String& string1, const char *string2)
{
String s;
s = string1;
s += string2;
aux = s;
return aux;
}
String& operator+ (const char *string1, const String& string2)
{
String s;
s = string1;
s += string2;
aux = s;
return aux;
}
// =============================================================================
// Sub-string extraction
String& String::Mid (int first, int count) const
{
if (count < 0)
count = 0;
else if (count > GetLength ())
count = GetLength ();
String s;
strncpy (s.GetBuffer (count+1), m_pData + first, count);
s.m_pData[count] = '\0';
aux = s;
return aux;
}
String& String::Left (int count) const
{
if (count < 0)
count = 0;
else if (count > GetLength ())
count = GetLength ();
String s;
strncpy (s.GetBuffer (count+1), m_pData, count);
s.m_pData[count] = '\0';
aux = s;
return aux;
}
String& String::Right (int count) const
{
if (count < 0)
count = 0;
else if (count > GetLength ())
count = GetLength ();
String s;
strncpy (s.GetBuffer (count+1), m_pData + GetLength () - count, count);
s.m_pData[count] = '\0';
aux = s;
return aux;
}
// =============================================================================
// Other functions
// Evaluates the contents of the string against a boolean expression.
// For example: (^Car | %Animal) & !Parrot
// Will return true for any strings that have the Car word or
// begin with Animal and do not have the word Parrot.
bool String::Match(const String& Expression) const
{
// Check if we need to split the test expression.
const char* p = Expression;
while (*p)
{
if (*p == '!')
{
return !Match(String(p + 1));
}
else if (*p == '(')
{
const char* Start = p;
int c = 0;
// Skip what's inside the parenthesis.
do
{
if (*p == '(')
c++;
else if (*p == ')')
c--;
else if (*p == 0)
return false; // Mismatched parenthesis.
p++;
}
while (c);
if (*p == 0)
break;
}
else if ((*p == '|') || (*p == '&'))
{
String Left, Right;
Left = Expression.Left((p - Expression) - 1);
Right = Expression.Right(Expression.GetLength() - (p - Expression) - 1);
if (*p == '|')
return Match(Left) || Match(Right);
else
return Match(Left) && Match(Right);
}
p++;
}
if (Expression.Find('(') != -1)
{
p = Expression;
while (*p)
{
if (*p == '(')
{
const char* Start = p;
int c = 0;
// Extract what's inside the parenthesis.
do
{
if (*p == '(')
c++;
else if (*p == ')')
c--;
else if (*p == 0)
return false; // Mismatched parenthesis.
p++;
}
while (c);
String Expr = Expression.Mid(Start - Expression + 1, p - Start - 2);
return Match(Expr);
}
p++;
}
}
// Testing a simple case.
String Search = Expression;
Search.TrimRight();
Search.TrimLeft();
const char* Word = Search;
// Check for modifiers.
bool WholeWord = 0;
bool Begin = 0;
for (;;)
{
if (Word[0] == '^')
WholeWord = true;
else if (Word[0] == '%')
Begin = true;
else
break;
Word++;
}
int Result = Find(Word);
if (Result == -1)
return false;
if (Begin && (Result != 0))
{
if ((Result != 1) || ((GetAt(Result-1) != '_') && (GetAt(Result-1) != '~')))
return false;
}
if (WholeWord)
{
char End = GetAt(Result + strlen(Word));
if ((End != 0) && (End != ' '))
return false;
if ((Result != 0) && ((GetAt(Result-1) == '_') || (GetAt(Result-1) == '~')))
Result--;
if ((Result != 0) && (GetAt(Result-1) != ' '))
return false;
}
return true;
}
int String::CompareNoCase (const char *string) const
{
char c1, c2, *ch = m_pData;
while (*ch && *string)
{
c1 = tolower (*ch);
c2 = tolower (*string);
if (c1 != c2)
return (c1 - c2);
ch++; string++;
}
return (((int)*ch) - ((int)*string));
}
int String::CompareNoCase(const char *string, int count) const
{
char c1, c2, *ch = m_pData;
while (*ch && *string)
{
c1 = tolower (*ch);
c2 = tolower (*string);
if (c1 != c2)
return (c1 - c2);
ch++;
string++;
count--;
if (!count)
return 0;
}
return (((int)*ch) - ((int)*string));
}
void String::MakeUpper ()
{
for (char *cp = m_pData; *cp; ++cp)
if ('a' <= *cp && *cp <= 'z')
*cp += 'A' - 'a';
}
void String::MakeLower ()
{
for (char *cp = m_pData; *cp; ++cp)
if ('A' <= *cp && *cp <= 'Z')
*cp += 'a' - 'A';
}
void String::MakeReverse ()
{
register char *h, *t;
h = m_pData;
t = m_pData + strlen (m_pData) - 1;
while (h < t)
{
register char c;
c = *h;
*h = *t;
h++;
*t = c;
t--;
}
}
void String::TrimRight ()
{
for (char *s = m_pData + strlen (m_pData) - 1; s >= m_pData && isspace (*s); s--)
*s = '\0';
}
void String::TrimLeft ()
{
char *ch;
ch = m_pData;
while (isspace (*ch))
ch++;
memmove (m_pData, ch, strlen (ch)+1);
}

157
common/str.h Normal file
View file

@ -0,0 +1,157 @@
#ifndef _STR_H_
#define _STR_H_
#include <string.h>
class String
{
public:
String();
String(const String& src)
{ m_pData = NULL; *this = src; }
String(const char* str)
{ m_pData = NULL; *this = str; }
~String();
int GetLength() const
{ return strlen(m_pData); }
bool IsEmpty() const
{ return m_pData[0] == '\0'; }
void Empty()
{ m_pData[0] = '\0'; }
char GetAt(int index) const
{ return m_pData[index]; }
char& operator[](int index) const
{ return m_pData[index]; }
void SetAt(int index, char ch)
{ m_pData[index] = ch; }
operator char*() const
{ return m_pData; }
operator const char*() const
{ return m_pData; }
// Operators
const String& operator=(const String& src);
const String& operator=(char ch);
const String& operator=(const char *src);
const String& operator+=(const String& string);
const String& operator+=(char ch);
const String& operator+=(const char *src);
// Comparison
int Compare(const char *string) const
{ return strcmp(m_pData, string); }
int CompareNoCase(const char *string) const;
int CompareNoCase(const char *string, int count) const;
bool Match(const String& Expression) const;
// simple sub-string extraction
String& Mid(int first, int count) const;
String& Mid(int first) const
{ return Mid(first, GetLength() - first); }
String& Left(int count) const;
String& Right(int count) const;
// upper/lower/reverse conversion
void MakeUpper();
void MakeLower();
void MakeReverse();
// trimming whitespace (either side)
void TrimRight();
void TrimLeft();
// searching (return starting index, or -1 if not found)
// look for a single character match
int Find(char ch) const
{
char *pf = strchr(m_pData, ch);
return (pf) ? (pf - m_pData) : -1;
}
int ReverseFind(char ch) const
{
char *pf = strrchr(m_pData, ch);
return (pf) ? (pf - m_pData) : -1;
}
int FindOneOf(const char *set) const
{
char *pf = strpbrk(m_pData, set);
return (pf) ? (pf - m_pData) : -1;
}
// look for a specific sub-string
int Find(const char *str) const
{
char *pf = strstr(m_pData, str);
return (pf) ? (pf - m_pData) : -1;
}
char* GetBuffer(int len)
{
if (len > (int)strlen(m_pData))
{
char *tmp = new char[len+1];
strcpy(tmp, m_pData);
delete []m_pData;
m_pData = tmp;
}
return m_pData;
}
void ReleaseBuffer(int len = -1)
{
if (len == -1)
len = strlen(m_pData);
m_pData[len] = '\0';
}
protected:
char* m_pData;
};
// Concatenation operators
String& operator+(const String& string1, const String& string2);
String& operator+(const String& string, char ch);
String& operator+(char ch, const String& string);
String& operator+(const String& string1, const char *string2);
String& operator+(const char *string1, const String& string2);
// Comparison operators
inline bool operator==(const String& s1, const String& s2)
{ return s1.Compare(s2) == 0; }
inline bool operator==(const String& s1, const char *s2)
{ return s1.Compare(s2) == 0; }
inline bool operator==(const char *s1, const String& s2)
{ return s2.Compare(s1) == 0; }
inline bool operator!=(const String& s1, const String& s2)
{ return s1.Compare(s2) != 0; }
inline bool operator!=(const String& s1, const char *s2)
{ return s1.Compare(s2) != 0; }
inline bool operator!=(const char *s1, const String& s2)
{ return s2.Compare(s1) != 0; }
inline bool operator<(const String& s1, const String& s2)
{ return s1.Compare(s2) < 0; }
inline bool operator<(const String& s1, const char *s2)
{ return s1.Compare(s2) < 0; }
inline bool operator<(const char *s1, const String& s2)
{ return s2.Compare(s1) > 0; }
inline bool operator>(const String& s1, const String& s2)
{ return s1.Compare(s2) > 0; }
inline bool operator>(const String& s1, const char *s2)
{ return s1.Compare(s2) > 0; }
inline bool operator>(const char *s1, const String& s2)
{ return s2.Compare(s1) < 0; }
inline bool operator<=(const String& s1, const String& s2)
{ return s1.Compare(s2) <= 0; }
inline bool operator<=(const String& s1, const char *s2)
{ return s1.Compare(s2) <= 0; }
inline bool operator<=(const char *s1, const String& s2)
{ return s2.Compare(s1) >= 0; }
inline bool operator>=(const String& s1, const String& s2)
{ return s1.Compare(s2) >= 0; }
inline bool operator>=(const String& s1, const char *s2)
{ return s1.Compare(s2) >= 0; }
inline bool operator>=(const char *s1, const String& s2)
{ return s2.Compare(s1) <= 0; }
#endif // _STR_H_

122
common/system.h Executable file
View file

@ -0,0 +1,122 @@
#ifndef _SYSTEM_H_
#define _SYSTEM_H_
#include "defines.h"
#include "typedefs.h"
// Assert macros.
#ifdef LC_DEBUG
extern bool lcAssert(const char* FileName, int Line, const char* Expression, const char* Description);
#define LC_ASSERT(Expr, Desc) \
do \
{ \
static bool Ignore = false; \
if (!Expr && !Ignore) \
Ignore = lcAssert(__FILE__, __LINE__, #Expr, Desc); \
} while (0)
#define LC_ASSERT_FALSE(Desc) LC_ASSERT(0, Desc)
#else
#define LC_ASSERT(expr, desc) do { } while(0)
#define LC_ASSERT_FALSE(Desc) LC_ASSERT(0, Desc)
#endif
// Profile functions
bool Sys_ProfileSaveInt (const char *section, const char *key, int value);
bool Sys_ProfileSaveString (const char *section, const char *key, const char *value);
int Sys_ProfileLoadInt (const char *section, const char *key, int default_value);
char* Sys_ProfileLoadString (const char *section, const char *key, const char *default_value);
// Memory render
void* Sys_StartMemoryRender (int width, int height);
void Sys_FinishMemoryRender (void* param);
// FIXME: moved to basewnd, remove
// Message Box
#define LC_OK 1
#define LC_CANCEL 2
#define LC_ABORT 3
#define LC_RETRY 4
#define LC_IGNORE 5
#define LC_YES 6
#define LC_NO 7
#define LC_MB_OK 0x000
#define LC_MB_OKCANCEL 0x001
//#define LC_MB_ABORTRETRYIGNORE 0x002
#define LC_MB_YESNOCANCEL 0x003
#define LC_MB_YESNO 0x004
//#define LC_MB_RETRYCANCEL 0x005
#define LC_MB_ICONERROR 0x010
#define LC_MB_ICONQUESTION 0x020
#define LC_MB_ICONWARNING 0x030
#define LC_MB_ICONINFORMATION 0x040
#define LC_MB_TYPEMASK 0x00F
#define LC_MB_ICONMASK 0x0F0
int Sys_MessageBox (const char* text, const char* caption="LeoCAD", int type=LC_MB_OK|LC_MB_ICONINFORMATION);
// FIXME end
// Misc stuff
bool Sys_KeyDown (int key);
class File;
class Camera;
class PieceInfo;
// User Interface
void SystemUpdateViewport(int nNew, int nOld);
void SystemUpdateAction(int nNew, int nOld);
void SystemUpdateColorList(int nNew);
void SystemUpdateRenderingMode(bool bBackground, bool bFast);
void SystemUpdateUndoRedo(char* undo, char* redo);
void SystemUpdateSnap(const unsigned long nSnap);
void SystemUpdateCurrentCamera(Camera* pOld, Camera* pNew, Camera* pCamera);
void SystemUpdateCameraMenu(Camera* pCamera);
void SystemUpdateTime(bool bAnimation, int nTime, int nTotal);
void SystemUpdateAnimation(bool bAnimation, bool bAddKeys);
void SystemUpdateSnap(unsigned short MoveSnap, unsigned short RotateSnap);
void SystemUpdateSelected(unsigned long flags, int SelectedCount, class Object* Focus);
void SystemUpdatePaste(bool enable);
void SystemUpdatePlay(bool play, bool stop);
void SystemUpdateCategories(bool SearchOnly);
void SystemInit();
void SystemFinish();
int SystemDoMessageBox(const char* prompt, int nMode);
bool SystemDoDialog(int nMode, void* param);
void SystemDoPopupMenu(int nMenu, int x, int y);
void SystemDoWaitCursor(int nCode);
void SystemSetWindowCaption(char* caption);
void SystemPieceComboAdd(char* name);
void SystemCaptureMouse();
void SystemReleaseMouse();
void SystemExportClipboard(File* clip);
File* SystemImportClipboard();
void SystemPumpMessages();
long SystemGetTicks();
void SystemStartProgressBar(int nLower, int nUpper, int nStep, const char* Text);
void SytemEndProgressBar();
void SytemStepProgressBar();
#endif // _SYSTEM_H_

861
common/terrain.cpp Normal file
View file

@ -0,0 +1,861 @@
// Terrain: a Bezier surface.
//
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "opengl.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;
}
/////////////////////////////////////////////////////////////////////////////
// TerrainPatch functions
TerrainPatch::TerrainPatch ()
{
vertex = NULL;
normals = NULL;
coords = NULL;
index = NULL;
steps = 10;
visible = true;
}
TerrainPatch::~TerrainPatch ()
{
FreeMemory ();
}
void TerrainPatch::InitBox(float minX, float maxX, float minY, float maxY, float minZ, float 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;
}
bool TerrainPatch::BoxIsOutside(const float plane[4]) const
{
float planeEqVal;
for (int i = 0; i < 8; i++)
{
planeEqVal = plane[0] * corners[i][0] + plane[1] * corners[i][1] + plane[2] * corners[i][2] + plane[3];
if (planeEqVal > 0)
return false;
}
return true;
}
#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 TerrainPatch::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 TerrainPatch::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->ReadByte (&ch, 1);
file->ReadLong (&i, 1);
file->ReadLong (&j, 1);
file->ReadFloat (&m_uSize, 1);
file->ReadFloat (&m_vSize, 1);
file->ReadLong (&m_nOptions, 1);
file->ReadFloat (&m_fColor, 3);
if (ch == 1)
{
file->Read(&ch, 1);
sh = ch;
}
else
file->ReadShort (&sh, 1);
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->ReadFloat (&m_pControl[i][j*3+2], 1);
}
void Terrain::FileSave(File* file)
{
unsigned char version = 2; // LeoCAD 0.70
unsigned short sh;
file->WriteByte (&version, 1);
file->WriteLong (&m_uPatches, 1);
file->WriteLong (&m_vPatches, 1);
file->WriteFloat (&m_uSize, 1);
file->WriteFloat (&m_vSize, 1);
file->WriteLong (&m_nOptions, 1);
file->WriteFloat (&m_fColor, 3);
sh = strlen (m_strTexture);
file->WriteShort (&sh, 1);
file->Write (m_strTexture, sh);
for (int i = 0; i < GetCountU(); i++)
for (int j = 0; j < GetCountV(); j++)
file->WriteFloat (&m_pControl[i][j*3+2], 1);
}
void Terrain::FreeMemory()
{
int i;
if (m_Patches)
{
for (i = 0; i < m_uPatches; i++)
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 TerrainPatch*[m_uPatches];
for (i = 0; i < m_uPatches; i++)
m_Patches[i] = new TerrainPatch[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 TerrainPatch*[m_uPatches];
for (i = 0; i < m_uPatches; i++)
m_Patches[i] = new TerrainPatch[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].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->GetEyePos(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->GetEyePos(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->GetUpVec(nonOrthoTop);
pCam->GetTargetPos(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 (m_Patches[i][j].BoxIsOutside(leftPlane))
{
m_Patches[i][j].visible = false;
continue;
}
if (m_Patches[i][j].BoxIsOutside(rightPlane))
{
m_Patches[i][j].visible = false;
continue;
}
if (m_Patches[i][j].BoxIsOutside(nearPlane))
{
m_Patches[i][j].visible = false;
continue;
}
if (m_Patches[i][j].BoxIsOutside(farPlane))
{
m_Patches[i][j].visible = false;
continue;
}
if (m_Patches[i][j].BoxIsOutside(bottomPlane))
{
m_Patches[i][j].visible = false;
continue;
}
if (m_Patches[i][j].BoxIsOutside(topPlane))
{
m_Patches[i][j].visible = false;
continue;
}
}
}
}
void Terrain::LoadDefaults(bool bLinear)
{
unsigned long rgb = Sys_ProfileLoadInt ("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, Sys_ProfileLoadString ("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();
}

83
common/terrain.h Normal file
View file

@ -0,0 +1,83 @@
//
// terrain.h
////////////////////////////////////////////////////
#ifndef _TERRAIN_H_
#define _TERRAIN_H_
#include "defines.h"
class File;
class Camera;
class Texture;
class TerrainPatch
{
public:
TerrainPatch ();
~TerrainPatch ();
float control[48]; // 4x4 grid
unsigned short steps;
float* vertex;
float* normals;
float* coords;
unsigned short* index;
bool visible;
void InitBox(float minX, float maxX, float minY, float maxY, float minZ, float maxZ);
bool BoxIsOutside(const float plane[4]) const;
void Tesselate(bool bNormals);
void FreeMemory();
protected:
float corners[8][3];
};
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;
TerrainPatch** m_Patches;
int m_uPatches;
int m_vPatches;
float m_uSize;
float m_vSize;
Texture* m_pTexture;
};
#endif // _TERRAIN_H_

160
common/texfont.cpp Normal file
View file

@ -0,0 +1,160 @@
//
// Texture Font
//
#include "globals.h"
#include "project.h"
#include "texfont.h"
#include "texture.h"
#include "library.h"
#include "file.h"
#include "lc_application.h"
#define LC_TEXFONT_FILE_VERSION 1 // LeoCAD 0.74
#define LC_TEXFONT_FILE_HEADER "LeoCAD Texture Font\0\0\0\0\0\0\0\0\0\0\0\0"
// ============================================================================
TexFont::TexFont ()
{
m_bLoaded = false;
m_pTexture = NULL;
memset (&m_Glyphs, 0, sizeof (m_Glyphs));
}
TexFont::~TexFont ()
{
if (m_pTexture != NULL)
m_pTexture->DeRef ();
}
bool TexFont::FileLoad (File& file)
{
unsigned char version;
char buf[64];
if (m_bLoaded)
{
console.PrintError ("Texture font already loaded.\n");
return false;
}
file.Read (buf, 32);
if (strncmp (buf, LC_TEXFONT_FILE_HEADER, 32) != 0)
{
console.PrintError ("Texture font file header mismatch.\n");
return false;
}
file.ReadByte (&version, 1);
if (version > LC_TEXFONT_FILE_VERSION)
{
console.PrintError ("Wrong version of texture font file.\n");
return false;
}
memset (buf, 0, 32);
file.Read (buf, 8);
m_pTexture = lcGetPiecesLibrary()->FindTexture (buf);
if (m_pTexture == NULL)
{
console.PrintError ("Cannot find texture for font %s.\n", buf);
return false;
}
m_pTexture->AddRef (false);
file.ReadByte (&m_nFontHeight, 1);
for (;;)
{
unsigned char glyph;
file.ReadByte (&glyph, 1);
if (glyph == 0)
break;
file.ReadByte (&m_Glyphs[glyph].width, 1);
file.ReadFloat (&m_Glyphs[glyph].left, 1);
file.ReadFloat (&m_Glyphs[glyph].right, 1);
file.ReadFloat (&m_Glyphs[glyph].top, 1);
file.ReadFloat (&m_Glyphs[glyph].bottom, 1);
m_Glyphs[glyph].left /= m_pTexture->m_nWidth;
m_Glyphs[glyph].right /= m_pTexture->m_nWidth;
m_Glyphs[glyph].top /= m_pTexture->m_nHeight;
m_Glyphs[glyph].bottom /= m_pTexture->m_nHeight;
}
m_bLoaded = true;
return true;
}
void TexFont::GetStringDimensions(int* cx, int* cy, const char* Text) const
{
*cx = 0;
*cy = m_nFontHeight;
while (*Text != 0)
{
*cx += m_Glyphs[(int)(*Text)].width;
Text++;
}
}
void TexFont::PrintText(float Left, float Top, float ScaleX, float ScaleY, const char* Text) const
{
float Height = m_nFontHeight * ScaleY;
while (*Text != 0)
{
int ch = *Text;
glTexCoord2f(m_Glyphs[ch].left, m_Glyphs[ch].top);
glVertex2f(Left, Top);
glTexCoord2f(m_Glyphs[ch].left, m_Glyphs[ch].bottom);
glVertex2f(Left, Top - Height);
glTexCoord2f(m_Glyphs[ch].right, m_Glyphs[ch].bottom);
glVertex2f(Left + m_Glyphs[ch].width * ScaleX, Top - Height);
glTexCoord2f(m_Glyphs[ch].right, m_Glyphs[ch].top);
glVertex2f(Left + m_Glyphs[ch].width * ScaleX, Top);
Left += m_Glyphs[ch].width * ScaleX;
Text++;
}
}
// Old function, should probably be removed.
void TexFont::PrintText(float Left, float Top, float Z, const char* Text) const
{
while (*Text != 0)
{
int ch = *Text;
glTexCoord2f(m_Glyphs[ch].left, m_Glyphs[ch].top);
glVertex3f(Left, Top, Z);
glTexCoord2f(m_Glyphs[ch].left, m_Glyphs[ch].bottom);
glVertex3f(Left, Top - m_nFontHeight, Z);
glTexCoord2f(m_Glyphs[ch].right, m_Glyphs[ch].bottom);
glVertex3f(Left + m_Glyphs[ch].width, Top - m_nFontHeight, Z);
glTexCoord2f(m_Glyphs[ch].right, m_Glyphs[ch].top);
glVertex3f(Left + m_Glyphs[ch].width, Top, Z);
Left += m_Glyphs[ch].width;
Text++;
}
}
// Temporary function to draw the axis icon text
void TexFont::PrintCharScaled (float scale, int ch) const
{
glTexCoord2f (m_Glyphs[ch].left, m_Glyphs[ch].top);
glVertex2f (-scale * m_Glyphs[ch].width, scale * m_nFontHeight);
glTexCoord2f (m_Glyphs[ch].left, m_Glyphs[ch].bottom);
glVertex2f (-scale * m_Glyphs[ch].width, -scale * m_nFontHeight);
glTexCoord2f (m_Glyphs[ch].right, m_Glyphs[ch].bottom);
glVertex2f (scale * m_Glyphs[ch].width, -scale * m_nFontHeight);
glTexCoord2f (m_Glyphs[ch].right, m_Glyphs[ch].top);
glVertex2f (scale * m_Glyphs[ch].width, scale * m_nFontHeight);
}

38
common/texfont.h Normal file
View file

@ -0,0 +1,38 @@
#ifndef _TEXFONT_H_
#define _TEXFONT_H_
class File;
class Texture;
#include "texture.h"
class TexFont
{
public:
TexFont ();
~TexFont ();
bool IsLoaded () const
{ return m_bLoaded; }
void MakeCurrent ()
{ if (m_bLoaded) m_pTexture->MakeCurrent (); }
bool FileLoad(File& file);
void PrintText(float left, float top, float z, const char* text) const;
void PrintText(float Left, float Top, float ScaleX, float ScaleY, const char* Text) const;
void PrintCharScaled(float scale, int ch) const;
void GetStringDimensions(int* cx, int* cy, const char* Text) const;
protected:
struct
{
unsigned char width;
float left, right, top, bottom;
} m_Glyphs[256];
Texture* m_pTexture;
unsigned char m_nFontHeight;
bool m_bLoaded;
};
#endif // _TEXFONT_H_

271
common/texture.cpp Normal file
View file

@ -0,0 +1,271 @@
// Texture object.
//
#include <string.h>
#include <stdlib.h>
#include "opengl.h"
#include "file.h"
#include "texture.h"
#include "project.h"
#include "globals.h"
#include "image.h"
#include "library.h"
#include "lc_application.h"
// =============================================================================
// Static functions
static void* ResizeImage (GLubyte* old_image, int components, int srcw, int srch, int destw, int desth)
{
int i, j, k;
float sx, sy;
GLubyte* new_image;
new_image = (GLubyte*)malloc (destw*desth*components*sizeof(GLubyte));
if (new_image == NULL)
return NULL;
if (destw > 1)
sx = (GLfloat) (srcw-1) / (GLfloat) (destw-1);
else
sx = (GLfloat) (srcw-1);
if (desth > 1)
sy = (GLfloat) (srch-1) / (GLfloat) (desth-1);
else
sy = (GLfloat) (srch-1);
for (i = 0; i < desth; i++)
{
GLint ii = (GLint)(i * sy);
for (j = 0; j < destw; j++)
{
GLint jj = (GLint)(j * sx);
GLubyte *src = old_image + (ii * srcw + jj) * components;
GLubyte *dst = new_image + (i * destw + j) * components;
for (k = 0; k < components; k++)
*dst++ = *src++;
}
}
return new_image;
}
/////////////////////////////////////////////////////////////////////////////
// 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->ReadShort(&m_nWidth, 1);
idx->ReadShort(&m_nHeight, 1);
idx->ReadByte(&bt, 1);
switch (bt)
{
case LC_INTENSITY:
m_nFormat = GL_LUMINANCE_ALPHA;
m_nFileSize = m_nWidth*m_nHeight;
break;
case LC_RGB:
m_nFormat = GL_RGB;
m_nFileSize = m_nWidth*m_nHeight*3;
break;
case LC_RGBA:
m_nFormat = GL_RGBA;
m_nFileSize = m_nWidth*m_nHeight*4;
break;
}
idx->ReadLong(&m_nOffset, 1);
}
void Texture::Unload()
{
if (m_nID != 0)
glDeleteTextures(1, &m_nID);
m_nID = 0;
}
// Load from textures.bin file
void Texture::Load(bool bFilter)
{
char filename[LC_MAXPATH];
FileDisk bin;
void* bits;
strcpy(filename, lcGetPiecesLibrary()->GetLibraryPath());
strcat(filename, "textures.bin");
if (!bin.Open(filename, "rb"))
return;
if (m_nFormat == GL_LUMINANCE_ALPHA)
bits = malloc (m_nFileSize*2);
else
bits = malloc (m_nFileSize);
bin.Seek (m_nOffset, SEEK_SET);
bin.Read (bits, m_nFileSize);
bin.Close ();
FinishLoadImage (bFilter, bits);
free(bits);
}
bool Texture::LoadFromFile (char* strFilename, bool bFilter)
{
Image image;
if (image.FileLoad (strFilename))
{
image.ResizePow2 ();
m_nWidth = image.Width ();
m_nHeight = image.Height ();
if (image.Alpha ())
m_nFormat = GL_RGBA;
else
m_nFormat = GL_RGB;
if (FinishLoadImage (bFilter, image.GetData ()) == true)
return true;
}
if (m_nID != 0)
{
glDeleteTextures(1, &m_nID);
m_nID = 0;
}
m_nWidth = 0;
m_nHeight = 0;
m_nFileSize = 0;
return false;
}
bool Texture::FinishLoadImage (bool bFilter, void *data)
{
GLint w, h, level, maxsize;
GLint i, j, k, pow2;
GLint components;
if (data == NULL || m_nWidth < 1 || m_nHeight < 1)
return false;
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);
switch (m_nFormat)
{
case GL_LUMINANCE_ALPHA: components = 2; break;
case GL_RGB: components = 3; break;
case GL_RGBA: components = 4; break;
default: return false;
}
// create an alpha channel for the texture
if (m_nFormat == GL_LUMINANCE_ALPHA)
for (i = m_nWidth*m_nHeight-1; i >= 0; i--)
((GLubyte*)data)[i*2+1] = ((GLubyte*)data)[i*2] = ((GLubyte*)data)[i];
glGetIntegerv (GL_MAX_TEXTURE_SIZE, &maxsize);
for (pow2 = 1; pow2 < m_nWidth; pow2 = pow2 << 1);
w = (pow2 == m_nWidth) ? m_nWidth : (pow2 << 1);
for (pow2 = 1; pow2 < m_nHeight; pow2 = pow2 << 1);
h = (pow2 == m_nHeight) ? m_nHeight : (pow2 << 1);
if (w > maxsize) w = maxsize;
if (h > maxsize) h = maxsize;
if (w != m_nWidth || h != m_nHeight)
{
data = ResizeImage ((GLubyte*)data, components, m_nWidth, m_nHeight, w, h);
m_nWidth = w;
m_nHeight = h;
if (data == NULL)
return false;
}
else
{
void *tmp = malloc (w*h*components);
memcpy (tmp, data, w*h*components);
data = tmp;
}
glTexImage2D (GL_TEXTURE_2D, 0, components, w, h, 0, m_nFormat, GL_UNSIGNED_BYTE, data);
if (bFilter)
for (level = 1; ((w != 1) || (h != 1)); level++)
{
GLubyte *out, *in;
int row;
row = w * components;
if (w != 1) w >>= 1;
if (h != 1) h >>= 1;
in = out = (GLubyte*)data;
for (i = 0; i < h; i++, in+=row)
for (j = 0; j < w; j++, out+=components, in+=2*components)
for (k = 0; k < components; k++)
out[k] = (in[k] + in[k+components] + in[row] + in[row+k+components])>>2;
glTexImage2D (GL_TEXTURE_2D, level, components, w, h, 0, m_nFormat, GL_UNSIGNED_BYTE, data);
}
free (data);
return true;
}

53
common/texture.h Normal file
View file

@ -0,0 +1,53 @@
//
// texture.h
////////////////////////////////////////////////////
#ifndef _TEXTURE_H
#define _TEXTURE_H
class File;
#include "opengl.h"
#include "config.h"
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 ((m_nID != 0) && (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];
lcuint16 m_nWidth;
lcuint16 m_nHeight;
protected:
bool FinishLoadImage (bool bFilter, void *data);
int m_nRef;
GLuint m_nID;
GLenum m_nFormat;
lcuint32 m_nOffset;
lcuint32 m_nFileSize;
};
#endif // _TEXTURE_H

325
common/tr.cpp Normal file
View file

@ -0,0 +1,325 @@
// TR.cpp: implementation of the TiledRender class.
//
//////////////////////////////////////////////////////////////////////
#include <math.h>
#include "opengl.h"
#include "tr.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()
{
GLint 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()
{
GLint 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
{
GLdouble modelview[16], proj[16];
GLint viewport[4];
GLdouble 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();
}
}
}

75
common/tr.h Normal file
View file

@ -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;
GLint m_ViewportSave[4];
};
#endif // _TR_H_

420
common/typedefs.h Normal file
View file

@ -0,0 +1,420 @@
// Typedefs.
//
#ifndef _TYPEDEF_H_
#define _TYPEDEF_H_
class Group;
class Piece;
class PieceInfo;
class Camera;
#include "defines.h"
#include "str.h"
#include "algebra.h"
typedef enum
{
LC_COLOR_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_ZOOM,
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_STEP_INSERT,
LC_VIEW_STEP_DELETE,
LC_VIEW_STOP,
LC_VIEW_PLAY,
LC_VIEW_CAMERA_FRONT,
LC_VIEW_CAMERA_BACK,
LC_VIEW_CAMERA_TOP,
LC_VIEW_CAMERA_BOTTOM,
LC_VIEW_CAMERA_LEFT,
LC_VIEW_CAMERA_RIGHT,
LC_VIEW_CAMERA_MAIN,
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_FASTRENDER,
LC_TOOLBAR_BACKGROUND,
LC_EDIT_MOVEXY_SNAP_0,
LC_EDIT_MOVEXY_SNAP_1,
LC_EDIT_MOVEXY_SNAP_2,
LC_EDIT_MOVEXY_SNAP_3,
LC_EDIT_MOVEXY_SNAP_4,
LC_EDIT_MOVEXY_SNAP_5,
LC_EDIT_MOVEXY_SNAP_6,
LC_EDIT_MOVEXY_SNAP_7,
LC_EDIT_MOVEXY_SNAP_8,
LC_EDIT_MOVEXY_SNAP_9,
LC_EDIT_MOVEZ_SNAP_0,
LC_EDIT_MOVEZ_SNAP_1,
LC_EDIT_MOVEZ_SNAP_2,
LC_EDIT_MOVEZ_SNAP_3,
LC_EDIT_MOVEZ_SNAP_4,
LC_EDIT_MOVEZ_SNAP_5,
LC_EDIT_MOVEZ_SNAP_6,
LC_EDIT_MOVEZ_SNAP_7,
LC_EDIT_MOVEZ_SNAP_8,
LC_EDIT_MOVEZ_SNAP_9,
LC_EDIT_ANGLE_SNAP_0,
LC_EDIT_ANGLE_SNAP_1,
LC_EDIT_ANGLE_SNAP_2,
LC_EDIT_ANGLE_SNAP_3,
LC_EDIT_ANGLE_SNAP_4,
LC_EDIT_ANGLE_SNAP_5,
LC_EDIT_ANGLE_SNAP_6,
LC_EDIT_ANGLE_SNAP_7,
LC_EDIT_ANGLE_SNAP_8,
LC_EDIT_ACTION_SELECT,
LC_EDIT_ACTION_INSERT,
LC_EDIT_ACTION_LIGHT,
LC_EDIT_ACTION_SPOTLIGHT,
LC_EDIT_ACTION_CAMERA,
LC_EDIT_ACTION_MOVE,
LC_EDIT_ACTION_ROTATE,
LC_EDIT_ACTION_ERASER,
LC_EDIT_ACTION_PAINT,
LC_EDIT_ACTION_ZOOM,
LC_EDIT_ACTION_ZOOM_REGION,
LC_EDIT_ACTION_PAN,
LC_EDIT_ACTION_ROTATE_VIEW,
LC_EDIT_ACTION_ROLL,
} LC_COMMANDS;
typedef enum
{
LC_ACTION_SELECT,
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_ACTION_CURVE
} LC_ACTIONS;
typedef enum
{
LC_CURSOR_NONE,
LC_CURSOR_BRICK,
LC_CURSOR_LIGHT,
LC_CURSOR_SPOTLIGHT,
LC_CURSOR_CAMERA,
LC_CURSOR_SELECT,
LC_CURSOR_SELECT_GROUP,
LC_CURSOR_MOVE,
LC_CURSOR_ROTATE,
LC_CURSOR_ROTATEX,
LC_CURSOR_ROTATEY,
LC_CURSOR_DELETE,
LC_CURSOR_PAINT,
LC_CURSOR_ZOOM,
LC_CURSOR_ZOOM_REGION,
LC_CURSOR_PAN,
LC_CURSOR_ROLL,
LC_CURSOR_ROTATE_VIEW,
LC_CURSOR_COUNT
} LC_CURSOR_TYPE;
// 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
{
Piece* piece;
Vector3 Position;
Vector3 Rotation;
char name[81];
int from;
int to;
bool hidden;
int color;
} LC_PIECE_MODIFY;
typedef struct
{
Camera* camera;
Vector3 Eye;
Vector3 Target;
Vector3 Up;
char name[81];
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_PNG,
LC_IMAGE_AVI
} LC_IMAGE_FORMATS;
typedef struct
{
unsigned char quality;
bool interlaced;
bool transparent;
bool truecolor;
unsigned char background[3];
float pause;
unsigned int 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_PROJECT,
LC_DLG_FILE_SAVE_PROJECT,
LC_DLG_FILE_MERGE_PROJECT,
LC_DLG_FILE_OPEN,
LC_DLG_FILE_SAVE,
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_EDITCATEGORY,
LC_DLG_ABOUT
} LC_DIALOGS;
typedef enum
{
LC_FILEOPENDLG_DAT,
LC_FILEOPENDLG_LCF,
LC_FILEOPENDLG_LUP
} LC_FILEOPENDLG_TYPES;
typedef struct
{
int type;
char path[LC_MAXPATH];
int numfiles;
char** filenames;
} LC_FILEOPENDLG_OPTS;
typedef enum
{
LC_FILESAVEDLG_LCF,
} LC_FILESAVEDLG_TYPES;
typedef struct
{
int type;
char path[LC_MAXPATH];
} LC_FILESAVEDLG_OPTS;
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 highlight;
bool htmlext;
LC_IMAGEDLG_OPTS imdlg;
} LC_HTMLDLG_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 strUser[101];
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;
typedef struct
{
String Name;
String Keywords;
} LC_CATEGORYDLG_OPTS;
#endif

105
common/vector.cpp Normal file
View file

@ -0,0 +1,105 @@
#include <math.h>
#include "vector.h"
#include "defines.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
Vector::Vector()
{
m_fPoint[0] = 0;
m_fPoint[1] = 0;
m_fPoint[2] = 0;
}
Vector::Vector(float x, float y, float z)
{
m_fPoint[0] = x;
m_fPoint[1] = y;
m_fPoint[2] = z;
}
Vector::Vector(const float *point)
{
m_fPoint[0] = point[0];
m_fPoint[1] = point[1];
m_fPoint[2] = point[2];
}
Vector::Vector(const float *p1, const float *p2)
{
m_fPoint[0] = p2[0] - p1[0];
m_fPoint[1] = p2[1] - p1[1];
m_fPoint[2] = p2[2] - p1[2];
}
//////////////////////////////////////////////////////////////////////
// Operations
Vector& Vector::operator*=(float scalar)
{
m_fPoint[0] *= scalar;
m_fPoint[1] *= scalar;
m_fPoint[2] *= scalar;
return *this;
}
Vector& Vector::operator+=(const 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-=(const Vector& sub)
{
m_fPoint[0] -= sub.m_fPoint[0];
m_fPoint[1] -= sub.m_fPoint[1];
m_fPoint[2] -= sub.m_fPoint[2];
return *this;
}
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 / Length();
m_fPoint[0] *= inv;
m_fPoint[1] *= inv;
m_fPoint[2] *= inv;
}
Vector& Vector::Cross(const Vector& v1, const 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(const Vector& vec)
{
double d, m1, m2;
d = m_fPoint[0]*vec.m_fPoint[0]+m_fPoint[1]*vec.m_fPoint[1]+m_fPoint[2]*vec.m_fPoint[2];
m1 = sqrt(m_fPoint[0]*m_fPoint[0]+m_fPoint[1]*m_fPoint[1]+m_fPoint[2]*m_fPoint[2]);
m2 = 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]);
return (float)(RTOD * acos(d / (m1*m2)));
}
float Vector::Dot(const 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)
{
point[0] = m_fPoint[0];
point[1] = m_fPoint[1];
point[2] = m_fPoint[2];
}

30
common/vector.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef _VECTOR_H_
#define _VECTOR_H_
class Vector
{
public:
Vector();
Vector(float x, float y, float z);
Vector(const float *point);
Vector(const float *p1, const float *p2);
~Vector() { };
float Dot(const Vector& vec);
float Angle(const Vector& vec);
Vector& Cross(const Vector& v1, const Vector& v2);
Vector& operator+=(const Vector& add);
Vector& operator-=(const Vector& sub);
Vector& operator*=(float scalar);
operator const float*() const
{ return m_fPoint; }
void ToFloat(float *point);
float Length();
void Normalize();
protected:
float m_fPoint[3];
};
#endif // _VECTOR_H_

139
common/view.cpp Normal file
View file

@ -0,0 +1,139 @@
//
// View the project contents
//
#include <stdlib.h>
#include "project.h"
#include "view.h"
#include "system.h"
View::View(Project *pProject, GLWindow *share)
: GLWindow(share)
{
m_Project = pProject;
}
View::~View()
{
if (m_Project != NULL)
m_Project->RemoveView(this);
}
LC_CURSOR_TYPE View::GetCursor(int Ptx, int Pty) const
{
// TODO: check if we're the focused window and return just the default arrow if we aren't.
switch (m_Project->GetAction())
{
case LC_ACTION_SELECT:
if (Sys_KeyDown(KEY_CONTROL))
return LC_CURSOR_SELECT_GROUP;
else
return LC_CURSOR_SELECT;
case LC_ACTION_INSERT:
return LC_CURSOR_BRICK;
case LC_ACTION_LIGHT:
return LC_CURSOR_LIGHT;
case LC_ACTION_SPOTLIGHT:
return LC_CURSOR_SPOTLIGHT;
case LC_ACTION_CAMERA:
return LC_CURSOR_CAMERA;
case LC_ACTION_MOVE:
return LC_CURSOR_MOVE;
case LC_ACTION_ROTATE:
return LC_CURSOR_ROTATE;
case LC_ACTION_ERASER:
return LC_CURSOR_DELETE;
case LC_ACTION_PAINT:
return LC_CURSOR_PAINT;
case LC_ACTION_ZOOM:
return LC_CURSOR_ZOOM;
case LC_ACTION_ZOOM_REGION:
return LC_CURSOR_ZOOM_REGION;
case LC_ACTION_PAN:
return LC_CURSOR_PAN;
case LC_ACTION_ROTATE_VIEW:
switch (m_Project->GetOverlayMode())
{
case LC_OVERLAY_X: return LC_CURSOR_ROTATEX;
case LC_OVERLAY_Y: return LC_CURSOR_ROTATEY;
case LC_OVERLAY_Z: return LC_CURSOR_ROLL;
case LC_OVERLAY_XYZ: return LC_CURSOR_ROTATE_VIEW;
default:
LC_ASSERT_FALSE("Unknown cursor type.");
return LC_CURSOR_NONE;
}
case LC_ACTION_ROLL:
return LC_CURSOR_ROLL;
case LC_ACTION_CURVE:
default:
LC_ASSERT_FALSE("Unknown cursor type.");
return LC_CURSOR_NONE;
}
}
void View::OnDraw()
{
MakeCurrent();
m_Project->SetViewSize(m_nWidth, m_nHeight);
m_Project->Render(false);
SwapBuffers();
}
void View::OnInitialUpdate()
{
GLWindow::OnInitialUpdate();
m_Project->AddView(this);
}
void View::OnLeftButtonDown(int x, int y, bool bControl, bool bShift)
{
m_Project->SetViewSize(m_nWidth, m_nHeight);
m_Project->OnLeftButtonDown(x, y, bControl, bShift);
}
void View::OnLeftButtonUp(int x, int y, bool bControl, bool bShift)
{
m_Project->SetViewSize(m_nWidth, m_nHeight);
m_Project->OnLeftButtonUp(x, y, bControl, bShift);
}
void View::OnLeftButtonDoubleClick(int x, int y, bool bControl, bool bShift)
{
m_Project->SetViewSize(m_nWidth, m_nHeight);
m_Project->OnLeftButtonDoubleClick(x, y, bControl, bShift);
}
void View::OnRightButtonDown(int x, int y, bool bControl, bool bShift)
{
m_Project->SetViewSize(m_nWidth, m_nHeight);
m_Project->OnRightButtonDown(x, y, bControl, bShift);
}
void View::OnRightButtonUp(int x, int y, bool bControl, bool bShift)
{
m_Project->SetViewSize(m_nWidth, m_nHeight);
m_Project->OnRightButtonUp(x, y, bControl, bShift);
}
void View::OnMouseMove(int x, int y, bool bControl, bool bShift)
{
m_Project->SetViewSize(m_nWidth, m_nHeight);
m_Project->OnMouseMove(x, y, bControl, bShift);
}

33
common/view.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef _VIEW_H_
#define _VIEW_H_
#include "glwindow.h"
class Project;
class View : public GLWindow
{
public:
View (Project *pProject, GLWindow *share);
virtual ~View ();
void OnDraw ();
void OnInitialUpdate ();
void OnLeftButtonDown (int x, int y, bool bControl, bool bShift);
void OnLeftButtonUp (int x, int y, bool bControl, bool bShift);
void OnLeftButtonDoubleClick (int x, int y, bool bControl, bool bShift);
void OnRightButtonDown (int x, int y, bool bControl, bool bShift);
void OnRightButtonUp (int x, int y, bool bControl, bool bShift);
void OnMouseMove (int x, int y, bool bControl, bool bShift);
LC_CURSOR_TYPE GetCursor(int x, int y) const;
Project* GetProject () const
{ return m_Project; }
protected:
Project* m_Project;
// virtual void OnInitialUpdate (); // called first time after construct
};
#endif // _VIEW_H_

385
config.mk Normal file
View file

@ -0,0 +1,385 @@
# LeoCAD configuration file
#
include version.mk
ERROR_SETTING=2> /dev/null
default: all
CC := gcc
CXX := g++
# (Add a -g for debugging)
CPPFLAGS += -O2 -Wall
# Add compile options, such as -I option to include jpeglib's headers
# CPPFLAGS += -I/home/fred/jpeglib
# Add linker options, such as -L option to include jpeglib's libraries
# LDFLAGS += -L/home/fred/jpeglib
### Linux configuration
ifeq ($(shell uname), Linux)
OS := -DLC_LINUX
OSDIR := linux
TEST_GTK := 1
endif
### FreeBSD configuration
ifeq ($(shell uname), FreeBSD)
OS := -DLC_LINUX
OSDIR := linux
TEST_GTK := 1
CPPFLAGS += -L/usr/local/lib
endif
### Macintosh configuration
ifeq ($(shell uname), Darwin)
OS := -DLC_MACOSX
OSDIR := macosx
TEST_GTK := 0
CPPFLAGS += -funsigned-char -I/Developer/Headers/FlatCarbon/
LDFLAGS += -framework AGL -framework Carbon
# The directory tree for the generated bundle
BUNDLEDIR = bin/LeoCAD.app
CONTDIR = $(BUNDLEDIR)/Contents
BINDIR = $(CONTDIR)/MacOS
RSRCDIR = $(CONTDIR)/Resources
EN_RSRCDIR = $(RSRCDIR)/English.lproj
NIBS = main.nib
all: bundletree exec bnibs pkginfo infoplist infopliststr
bundletree: $(BINDIR) $(BUNDLEDIR) $(CONTDIR) $(RSRCDIR) $(EN_RSRCDIR)
$(BUNDLEDIR):
mkdir bin
mkdir $(BUNDLEDIR)
$(CONTDIR): $(BUNDLEDIR)
mkdir $(CONTDIR)
$(BINDIR): $(CONTDIR)
mkdir $(BINDIR)
$(RSRCDIR): $(CONTDIR)
mkdir $(RSRCDIR)
$(EN_RSRCDIR): $(RSRCDIR)
mkdir $(EN_RSRCDIR)
# The binary executable
exec: $(BINDIR)/leocad
$(BINDIR)/leocad: bin/leocad
cp bin/leocad $(BINDIR)
# The nib's
BNIBS = $(NIBS:%=$(EN_RSRCDIR)/%)
bnibs: $(BNIBS)
$(BNIBS) : $(EN_RSRCDIR)/%: macosx/English.lproj/%
rm -rf $@
cp -R $< $@
touch $@
# Info.plist
infoplist : $(CONTDIR)/Info.plist
$(CONTDIR)/Info.plist : macosx/Info.plist
cp macosx/Info.plist $@
# PkgInfo
pkginfo : $(CONTDIR)/PkgInfo
$(CONTDIR)/PkgInfo : macosx/PkgInfo
cp macosx/PkgInfo $@
# InfoPlist.strings - this goes inside English.lproj
infopliststr : $(EN_RSRCDIR)/InfoPlist.strings
$(EN_RSRCDIR)/InfoPlist.strings: macosx/English.lproj/InfoPlist.strings
cp $< $@
endif
### Default directory
ifeq ($(PREFIX), )
PREFIX := /usr/local
endif
.PHONY: config config-help
config-help:
@echo "This target attempts to detect your system settings,"
@echo "it will create $(OSDIR)/config.mk and $(OSDIR)/config.h"
@echo "Valid parameters and their default values are:"
@echo " PREFIX=/usr/local"
@echo " DESTDIR="
### Automatic configuration
#USE this with printf and a primitive type - printf "WIDTHTEST" "char" >conftest.c
CONFTEST="\#include <stdio.h>\nint main() { FILE *f=fopen(\"conftestval\", \"w\");\n\
if (!f) return 1; fprintf(f, \"%%d\\\n\", (int)sizeof(%s)); return 0; }\n"
config:
@echo "Automatic configuration"
@echo "### LeoCAD configuration" > $(OSDIR)/config.mk
@echo "### Auto-generated file, DO NOT EDIT" >> $(OSDIR)/config.mk
@echo "" >> $(OSDIR)/config.mk
@echo "PREFIX := $(PREFIX)" >> $(OSDIR)/config.mk;
@echo "DESTDIR := $(DESTDIR)" >> $(OSDIR)/config.mk;
@echo "" >> $(OSDIR)/config.mk
@echo "//" > $(OSDIR)/config.h
@echo "// LeoCAD configuration" >> $(OSDIR)/config.h
@echo "//" >> $(OSDIR)/config.h
@echo "// Auto-generated file, DO NOT EDIT" >> $(OSDIR)/config.h
@echo "//" >> $(OSDIR)/config.h
@echo "" >> $(OSDIR)/config.h
@echo "#ifndef _CONFIG_H_" >> $(OSDIR)/config.h
@echo "#define _CONFIG_H_" >> $(OSDIR)/config.h
@echo "" >> $(OSDIR)/config.h
### Version information
@echo "#define LC_VERSION_MAJOR $(MAJOR)" >> $(OSDIR)/config.h
@echo "#define LC_VERSION_MINOR $(MINOR)" >> $(OSDIR)/config.h
@echo "#define LC_VERSION_PATCH $(PATCHLVL)" >> $(OSDIR)/config.h
@echo "#define LC_VERSION_OSNAME \"$(shell uname)\"" >> $(OSDIR)/config.h
@echo "#define LC_VERSION_TEXT \"$(MAJOR).$(MINOR).$(PATCHLVL)\"" >> $(OSDIR)/config.h
@echo "#define LC_VERSION_TAG \"$(VERSIONTAG)\"" >> $(OSDIR)/config.h
@echo "#define LC_INSTALL_PREFIX \"$(PREFIX)\"" >> $(OSDIR)/config.h
@echo "" >> $(OSDIR)/config.h
### Determine variable sizes
@echo -n "checking size of char... "; \
printf ${CONFTEST} "char" > conftest.c; \
if { (eval $(CC) conftest.c -o conftest); } && \
(test -s conftest && (./conftest; exit)); then \
ac_cv_sizeof_char=`cat conftestval`; \
echo "$$ac_cv_sizeof_char"; \
else \
echo "failed to get size of char"; \
ac_cv_sizeof_char=0; \
fi; \
echo "#define LC_SIZEOF_CHAR $$ac_cv_sizeof_char" >> $(OSDIR)/config.h; \
rm -f conftest.c conftest conftestval; \
\
echo -n "checking size of short... "; \
printf ${CONFTEST} "short" > conftest.c; \
if { (eval $(CC) conftest.c -o conftest); } && \
(test -s conftest && (./conftest; exit)); then \
ac_cv_sizeof_short=`cat conftestval`; \
echo "$$ac_cv_sizeof_short"; \
else \
echo "failed to get size of short"; \
ac_cv_sizeof_short=0; \
fi; \
echo "#define LC_SIZEOF_SHORT $$ac_cv_sizeof_short" >> $(OSDIR)/config.h; \
rm -f conftest.c conftest conftestval; \
\
echo -n "checking size of long... "; \
printf ${CONFTEST} "long" > conftest.c; \
if { (eval $(CC) conftest.c -o conftest); } && \
(test -s conftest && (./conftest; exit)); then \
ac_cv_sizeof_long=`cat conftestval`; \
echo "$$ac_cv_sizeof_long"; \
else \
echo "failed to get size of long"; \
ac_cv_sizeof_long=0; \
fi; \
echo "#define LC_SIZEOF_LONG $$ac_cv_sizeof_long" >> $(OSDIR)/config.h; \
rm -f conftest.c conftest conftestval; \
\
echo -n "checking size of int... "; \
printf ${CONFTEST} "int" > conftest.c; \
if { (eval $(CC) conftest.c -o conftest); } && \
(test -s conftest && (./conftest; exit)); then \
ac_cv_sizeof_int=`cat conftestval`; \
echo "$$ac_cv_sizeof_int"; \
else \
echo "failed to get size of int"; \
ac_cv_sizeof_int=0; \
fi; \
echo "#define LC_SIZEOF_INT $$ac_cv_sizeof_int" >> $(OSDIR)/config.h; \
rm -f conftest.c conftest conftestval; \
\
echo -n "checking size of void *... "; \
printf ${CONFTEST} "void *" > conftest.c; \
if { (eval $(CC) conftest.c -o conftest); } && \
(test -s conftest && (./conftest; exit)); then \
ac_cv_sizeof_void_p=`cat conftestval`; \
echo "$$ac_cv_sizeof_void_p"; \
else \
echo "failed to get size of void *"; \
ac_cv_sizeof_void_p=0; \
fi; \
echo "#define LC_SIZEOF_VOID_P $$ac_cv_sizeof_void_p" >> $(OSDIR)/config.h; \
if test "$$ac_cv_sizeof_void_p" -eq "8"; then \
echo "#define LC_POINTER_TO_INT(p) ((lcint32)(lcint64)(p))" >> $(OSDIR)/config.h; \
else \
echo "#define LC_POINTER_TO_INT(p) ((lcint32)(p))" >> $(OSDIR)/config.h; \
fi; \
rm -f conftest.c conftest conftestval; \
\
echo -n "checking size of long long... "; \
printf ${CONFTEST} "long long" > conftest.c; \
if { (eval $(CC) conftest.c -o conftest); } && \
(test -s conftest && (./conftest; exit)); then \
ac_cv_sizeof_long_long=`cat conftestval`; \
echo "$$ac_cv_sizeof_long_long"; \
else \
echo "failed to get size of long long"; \
ac_cv_sizeof_long_long=0; \
fi; \
echo "#define LC_SIZEOF_LONG_LONG $$ac_cv_sizeof_long_long" >> $(OSDIR)/config.h; \
rm -f conftest.c conftest conftestval; \
case 2 in \
$$ac_cv_sizeof_short) lcint16=short;; \
$$ac_cv_sizeof_int) lcint16=int;; \
esac; \
case 4 in \
$$ac_cv_sizeof_short) lcint32=short;; \
$$ac_cv_sizeof_int) lcint32=int;; \
$$ac_cv_sizeof_long) lcint32=long;; \
esac; \
case 8 in \
$$ac_cv_sizeof_long_long) lcint64="long long";; \
esac; \
echo "" >> $(OSDIR)/config.h; \
echo "typedef signed char lcint8;" >> $(OSDIR)/config.h; \
echo "typedef unsigned char lcuint8;" >> $(OSDIR)/config.h; \
if test -n "$$lcint16"; then \
echo "typedef signed $$lcint16 lcint16;" >> $(OSDIR)/config.h; \
echo "typedef unsigned $$lcint16 lcuint16;" >> $(OSDIR)/config.h; \
else \
echo "#error need to define lcint16 and lcuint16" >> $(OSDIR)/config.h; \
fi; \
if test -n "$$lcint32"; then \
echo "typedef signed $$lcint32 lcint32;" >> $(OSDIR)/config.h; \
echo "typedef unsigned $$lcint32 lcuint32;" >> $(OSDIR)/config.h; \
else \
echo "#error need to define lcint32 and lcuint32" >> $(OSDIR)/config.h; \
fi; \
if test -n "$$lcint64"; then \
echo "typedef signed $$lcint64 lcint64;" >> $(OSDIR)/config.h; \
echo "typedef unsigned $$lcint64 lcuint64;" >> $(OSDIR)/config.h; \
else \
echo "#error need to define lcint64 and lcuint64" >> $(OSDIR)/config.h; \
fi; \
echo "" >> $(OSDIR)/config.h
### Check if machine is little or big endian
@echo -n "Determining endianess... "
@echo "int main () { union { long l; char c[sizeof (long)]; } u;" > endiantest.c
@echo "u.l = 1; return (u.c[sizeof (long) - 1] == 1); }" >> endiantest.c
@if { (eval $(CC) endiantest.c -o endiantest); } && \
(test -s endiantest && (./endiantest; exit)); then \
echo "little endian"; \
echo "#define LC_LITTLE_ENDIAN" >> $(OSDIR)/config.h; \
echo "#define LCUINT16(val) val" >> $(OSDIR)/config.h; \
echo "#define LCUINT32(val) val" >> $(OSDIR)/config.h; \
echo "#define LCINT16(val) val" >> $(OSDIR)/config.h; \
echo "#define LCINT32(val) val" >> $(OSDIR)/config.h; \
echo "#define LCFLOAT(val) val" >> $(OSDIR)/config.h; \
else \
echo "big endian"; \
echo "#define LC_BIG_ENDIAN" >> $(OSDIR)/config.h; \
echo "#define LCUINT16(val) ((lcuint16) ( \\" >> $(OSDIR)/config.h; \
echo " (((lcuint16) (val) & (lcuint16) 0x00ffU) << 8) | \\" >> $(OSDIR)/config.h; \
echo " (((lcuint16) (val) & (lcuint16) 0xff00U) >> 8)))" >> $(OSDIR)/config.h; \
echo "#define LCUINT32(val) ((lcuint32) ( \\" >> $(OSDIR)/config.h; \
echo " (((lcuint32) (val) & (lcuint32) 0x000000ffU) << 24) | \\" >> $(OSDIR)/config.h; \
echo " (((lcuint32) (val) & (lcuint32) 0x0000ff00U) << 8) | \\" >> $(OSDIR)/config.h; \
echo " (((lcuint32) (val) & (lcuint32) 0x00ff0000U) >> 8) | \\" >> $(OSDIR)/config.h; \
echo " (((lcuint32) (val) & (lcuint32) 0xff000000U) >> 24)))" >> $(OSDIR)/config.h; \
echo "#define LCINT16(val) ((lcint16)LCUINT16(val))" >> $(OSDIR)/config.h; \
echo "#define LCINT32(val) ((lcint32)LCUINT32(val))" >> $(OSDIR)/config.h; \
echo -e "inline float LCFLOAT (float l)\n{" >> $(OSDIR)/config.h; \
echo -e " union { unsigned char b[4]; float f; } in, out;\n" >> $(OSDIR)/config.h; \
echo -e " in.f = l;\n out.b[0] = in.b[3];\n out.b[1] = in.b[2];" >> $(OSDIR)/config.h; \
echo -e " out.b[2] = in.b[1];\n out.b[3] = in.b[0];\n" >> $(OSDIR)/config.h; \
echo -e " return out.f;\n}" >> $(OSDIR)/config.h; \
fi; \
echo "" >> $(OSDIR)/config.h
@rm -f endiantest.c endiantest
#### Check if the user has GTK+ and GLIB installed.
ifeq ($(TEST_GTK), 1)
@echo -n "Checking if GLIB and GTK+ are installed... "
@if (pkg-config --atleast-version=2.0.0 glib-2.0) && (pkg-config --atleast-version=2.0.0 gtk+-2.0); then \
echo "ok"; \
echo "CFLAGS += \$$(shell pkg-config gtk+-2.0 --cflags)" >> $(OSDIR)/config.mk; \
echo "CXXFLAGS += \$$(shell pkg-config gtk+-2.0 --cflags)" >> $(OSDIR)/config.mk; \
echo "LIBS += \$$(shell pkg-config gtk+-2.0 --libs)" >> $(OSDIR)/config.mk; \
else \
echo "failed"; \
rm -rf $(OSDIR)/config.mk $(OSDIR)/config.h; \
exit 1; \
fi
endif
## Check if the user has libjpeg installed
@echo -n "Checking for jpeg support... "
@echo "char jpeg_read_header();" > jpegtest.c
@echo "int main() { jpeg_read_header(); return 0; }" >> jpegtest.c
@if { (eval $(CC) jpegtest.c -ljpeg -o jpegtest $(CPPFLAGS) $(LDFLAGS)); } && \
(test -s jpegtest); then \
echo "ok"; \
echo "HAVE_JPEGLIB = yes" >> $(OSDIR)/config.mk; \
echo "#define LC_HAVE_JPEGLIB" >> $(OSDIR)/config.h; \
else \
echo "no (libjpeg optional)"; \
echo "HAVE_JPEGLIB = no" >> $(OSDIR)/config.mk; \
echo "#undef LC_HAVE_JPEGLIB" >> $(OSDIR)/config.h; \
fi
@rm -f jpegtest.c jpegtest
### Check if the user has zlib installed
@echo -n "Checking for zlib support... "
@echo "char gzread();" > ztest.c
@echo "int main() { gzread(); return 0; }" >> ztest.c
@if { (eval $(CC) ztest.c -lz -o ztest $(CPPFLAGS) $(LDFLAGS)); } && \
(test -s ztest); then \
echo "ok"; \
echo "HAVE_ZLIB = yes" >> $(OSDIR)/config.mk; \
echo "#define LC_HAVE_ZLIB" >> $(OSDIR)/config.h; \
else \
echo "no (zlib optional)"; \
echo "HAVE_ZLIB = no" >> $(OSDIR)/config.mk; \
echo "#undef LC_HAVE_ZLIB" >> $(OSDIR)/config.h; \
fi
@rm -f ztest.c ztest
### Check if the user has libpng installed
@echo -n "Checking for png support... "
@echo "char png_read_info();" > pngtest.c
@echo "int main() { png_read_info(); return 0; }" >> pngtest.c
@if { (eval $(CC) pngtest.c -lm -lz -lpng -o pngtest $(CPPFLAGS) $(LDFLAGS)); } && \
(test -s pngtest); then \
echo "ok"; \
echo "HAVE_PNGLIB = yes" >> $(OSDIR)/config.mk; \
echo "#define LC_HAVE_PNGLIB" >> $(OSDIR)/config.h; \
else \
echo "no (libpng optional)"; \
echo "HAVE_PNGLIB = no" >> $(OSDIR)/config.mk; \
echo "#undef LC_HAVE_PNGLIB" >> $(OSDIR)/config.h; \
fi
@rm -f pngtest.c pngtest
@echo "" >> $(OSDIR)/config.h
@echo "#endif // _CONFIG_H_" >> $(OSDIR)/config.h

5
description-pak Normal file
View file

@ -0,0 +1,5 @@
LeoCAD is a CAD program that allows people to build virtual models using
bricks similar to those found in LEGO toys. It's available for free under
the GNU Public License, and works on the Windows and Linux Operating
Systems.

206
docs/CHANGES.txt Normal file
View file

@ -0,0 +1,206 @@
This is a changelog for developers only, not for regular users.
30/07/2003
- Added MPD support.
17/10/2002
- Fixed several bugs with the 3DS exporter.
10/10/2002
- Added a dialog to handle the textures in the library.
26/08/2002
- Added an option to print the total pages on the header (&O).
- Fixed a bug with the step numbers when printing.
09/03/2002
- Moved the texture font to a class to allow support for multiple fonts.
08/03/2002
- Fixed a crash in the Edit Groups Dialog.
03/12/2001
- Added a console window for text messages.
30/07/2001
- Fixed a small memory leak and improved the default mouse sensitivity.
16/05/2001
- Fixed the way pieces are moved/rotated with the side cameras.
26/03/2001
- Reorganized the Image I/O functions, created a new Image class.
- Fixed the return value of FileMem::Seek.
23/03/2001
- Update object keys when adding/removing steps.
- Added an option to save HTML files with .html extension instead of .htm.
21/03/2001
- Improved Linux About dialog.
15/03/2001
- Fixed drawing area border not being drawn correctly.
13/03/2001
- Fixed texture font not visible on some video cards.
21/02/2001
- New shortcuts: Home/End keys will change the Pieces List selection (win32).
- Fixed the creation and destruction of the MainWnd under win32.
19/02/2001
- The "Mouse moves in 3D" option now applies to rotations too.
04/02/2001
- When multiple pieces are selected, rotate around the piece that has focus.
- Clicking on a piece with focus now deselects it.
- Only define LC_HAVE_PNGLIB if zlib is available.
- Don't compile jpeg or png support if libraries are not present.
02/02/2001
- HTML dialog now correctly remembers the last settings
- Added step add/remove commands
01/02/2001
- Fixed a crash deselecting point lights
07/01/2001
- Added a new command to create floating views of the project
05/01/2001
- Added a new cross platform class for handling OpenGL windows
- Added a new base window class for handling top level windows
02/01/2001
- Linux PPC fixes - LeoCAD now works on big endian computers !
Thanks to Lon Shapiro for allowing me to use his Mac for debugging.
30/12/2000
- Added Step Insert and Step Delete commands.
29/12/2000
- Capture mouse events while dragging the cursor under linux.
- HTML Dialog improvements under linux.
28/12/2000
- Click on "Last Step" goes to the last used step instead of step 255.
- Save HTML dialog options.
24/12/2000
- Fixed piece combo under linux.
20/12/2000
- Fixed crash when saving pictures of multiple steps.
================================================================================
Version 0.73
================================================================================
18/12/2000
- Fixed win32 help file.
17/12/2000
- Fixed win2k startup crash.
16/12/2000
- Fixed AVI saving.
- Fixed rotation bug.
15/12/2000
- Fixed small bug in the texture font.
03/12/2000
- Added Save Picture dialog to the linux version.
- Added an overwrite prompt when saving a project under linux.
02/12/2000
- Added project loading preview to the linux version.
24/11/2000
- Renamed several functions from the Matrix class.
- Added const to several functions.
23/11/2000
- Now all object classes have a Select () function.
18/11/2000
- Fixed animation playback under linux.
- Fixed Z buffer bug in the piece preview.
- Fixed blending in piece preview.
12/11/2000
- Added the PtrArray class.
- Started adding the internal messaging system.
- Link with g++ instead of gcc.
- Moved all keyframer functions to the Object class.
09/11/2000
- First implementation of the light classes.
- Added a const float* operator to the Vector class.
- Added functions to create transformation matrixes.
- Faster rendering of the camera objects.
- Added new minifig pieces.
01/10/2000
- Added an option to save custom minifigs to the Minifig Wizard.
30/09/2000
- Enabled the "Make Default" button of the preferences dialog (Linux).
24/09/2000
- Finished adding the new minifig rotation options.
20/09/2000
- Fixed the numeric keypad under Linux, thanks to Renaud Breard.
- Updated the Array dialog.
18/09/2000
- Added new menu shortcuts.
- Fixed endianess bugs when loading piece information.
- Fixed piece preview mirrored image.
- Fixed some warning messages in FreeBSD.
- Added endian test to auto-configuration.
- Fixed gtk-config problem under FreeBSD.
16/09/2000
- Added man page, thanks to Patrick Mahoney.
- Moved gif and jpg functions from image.cpp to separate files.
- Modified the Makefile to automatically check for libjpeg, zlib and
libpng. It also creates a config.h file with the options.
15/09/2000
- Fixed problem with "focus follow mouse" in X, thanks to Renaud Breard.
13/09/2000
- Fixed bug that caused image files to be saved upside down.
12/09/2000
- Fixed bug when exporting POV-Ray files using LGEO.
- Changed GL library name under Linux from 'libGL.so.1' to 'libGL.so'.
27/08/2000
- Save window position and size under Linux.
25/08/2000
- Changes to the Linux version:
- The Pieces toolbar can now be resized when docked or detached from
the main window.
- The toolbar menu now has the checkboxes correct.
- Remember toolbar state between sessions.
24/08/2000
- Updated the Windows version with the new Minifig Wizard.
- Fixed OpenGL problems on the Windows version.
21/08/2000
- New options for the Minifig Wizard, now each piece can be rotated.
19/08/2000
- Fixed a bug when you insert a piece after a rotated piece wouldn't
show the new piece correctly.
- Added a new parameter to Project::Initialize to search the program
directory for the piece library. Useful if a Linux user wants to
install everything in the same directory and keeps compatibility
with installations in /usr/local/share/leocad.
- Initial attempt of a BeOS port.

340
docs/COPYING.txt Normal file
View file

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

26
docs/INSTALL.txt Normal file
View file

@ -0,0 +1,26 @@
================
COMPILING LeoCAD
================
Linux/GNU platforms
-------------------
All your config options go into config.mk
You will need jpeglib. Install compile/install it and add it's header
directory as a -I to CPPFLAGS and -L to LDFLAGS. This isn't required if
it is installed to "standard" locations, like /usr/include and /usr/lib
Then just type MAKE
MS Windows
----------
- Extract LIBJPEG 6.0 to the win/libjpeg directory
- Extract ZLIB to win/zlib
- Extract LIBPNG to win/libpng
- Extract the 3DSFTK to win/3dsftk (use -d to keep directory names)
- Open VC++ and do a 'Make All'

89
docs/LINUX.txt Normal file
View file

@ -0,0 +1,89 @@
LeoCAD for Linux
----------------
This is the only documentation you will find for the Linux
version, I'll start writing some help files in HTML format soon but
it's going to take a while (I really prefer to code and my english
is not perfect). I strongly advise you to follow the online tutorial
from my web page, it will at least get you started with the basic
editing commands.
I still have to finish some dialogs, the printing routines
and a few GUI items but all the rest should work fine. I'd also
like to warn you that this is my first Linux program so if you
find anything that doesn't behave as you expect, a bug or just
have some suggestions, feel free to email me at
leonardo@centroin.com.br
I hope you enjoy using LeoCAD as much as I enjoyed writing it,
Leonardo
HOW TO GET HELP
---------------
The best source of help is the LeoCAD mailing list at
leocad@gerf.org
You can subscribe/unsubscribe at
http://gerf.org/mailman/listinfo/leocad
INSTALLATION
------------
Instalation should be very simple for most systems, first you need
get the executable and the pieces library from my homepage (if you
already have the library for the MS Windows version, you don't
need to download it again).
Now you should unzip the files to a temporary directory (you
probably already did that) and move the file 'leocad' to
/usr/local/bin/ and the 4 files from the pieces library to
/usr/local/share/leocad/. If you wish to have the library files
installed in another place, set the environment variable LEOCAD_LIB
to that directory or start the program with the -l <path> option.
You need to have an OpenGL library, you can download Mesa 3D
from www.mesa3d.org if you don't have one already and you will also
need GTK+ 1.2, it's available from www.gtk.org
If you're using the precompiled version, then you will also need
libjpeg, zlib and libpng. If you're compiling from the sources, the
configuration utility will detect what libraries you have installed
and only support those file formats.
Current versions used are:
GTK+ 1.2 http://www.gtk.org/
MESA 3D 3.4 http://www.mesa3d.org/
LIBJPEG 6.0 ftp://ftp.uu.net/graphics/jpeg/
LIBPNG 1.0 http://www.libpng.org/pub/png/libpng.html
ZLIB 1.1 http://www.info-zip.org/pub/infozip/zlib/
COMMAND LINE OPTIONS
--------------------
The man (1) page has a more complete description of all command line
parameters available. This is just a list of the most used ones:
[filename]: Loads a file
-l <path>: Tells the program to use the piece library located in the directory
pointed by <path>
-i [filename]: Saves a picture and exit
-w <x>: Use this width for the picture
-h <y>: Use this height for the picture
Example:
leocad car.lcd -i car.gif -w 640 -h 480 -l /mnt/c/leocad/
This will start LeoCAD, load the file "car.lcd", create a GIF file
called "car.gif" with a resolution of 640 x 480 using the pieces
library in /mnt/c/leocad/ and exit when done.

24
docs/TODO.txt Normal file
View file

@ -0,0 +1,24 @@
IDEAS:
- Add arrows & text objects
- Technic style Instructions (picture with new pcs.)
- Add lighting
- Collision detection (fix)
- Orthographic projections
- Choose rotation centers
- Flyby and explode (like Lego Creator)
- Clouds/stars/waves
- Network building
- Create HTML catalog of pieces
- OLE (probably not)
KNOWN BUGS:
- WIN: color lb tooltip bug (after calling a dialog)
- save piece list m_nLast in projects ?
- create backups
NEW (since 0.71.1)
- POV export bug
- paste step
- piecelist sort bug (linux)
- CS_OWNDC (win)

118
docs/leocad.1 Normal file
View file

@ -0,0 +1,118 @@
.TH LEOCAD 1 "20 July 2000"
.\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection
.\" other parms are allowed: see man(7), man(1)
.SH NAME
LeoCAD \- CAD program using plastic bricks.
.SH SYNOPSIS
\fIleocad \fR[\fI infile \fR[ -i \fR[\fI outfile.ext \fR]\fI \fR[\fI-w x\fR]\fI \fR[\fI-h y\fR] ] \fR] [\fI\-l path\fR]
.SH "DESCRIPTION"
This manual page documents briefly the \fILeoCAD \fRprogram.
It was written for the Debian GNU/Linux distribution
because the original program does not have a manual page. The information
in this manual page came from docs/LINUX.txt in the source archive.
.PP
\fILeoCAD \fR is a CAD program that uses plastic bricks similar to those found
in many toys (but not any particular brand). It supports animation
as well as exportation to various file formats including POV-Ray.
.SH OPTIONS
Below is a summary of all of the options that \fILeoCAD\fR
accepts. Most options have two equivalent names, one of
which is a single letter preceded by -, and the other of
which is a long name preceded by --. Brackets ([ and ])
indicate that an option takes an optional argument.
.TP
.B infile
Loads the project file \fIinfile\fR
.TP
.BI "\-i [" outfile.ext ]
.ns
.TP
.BI "\-\-image [" outfile.ext ]
Saves a picture to \fIoutfile.ext \fR in the image format specified
by \fIext\fR and exits. If outfile is not given then the program will
use a name based on \fIinfile\fR. The file format is selected by \fIext\fR
and can be gif, bmp, png or jpg (if \fIext\fR is not present then the
default preferences format will be used).
.TP
.BI "\-w "width
.ns
.TP
.BI "\-\-width "width
Sets the width of the picture to \fIwidth\fR.
.TP
.BI "\-h "height
.ns
.TP
.BI "\-\-height "height
Sets the height of the picture to \fIheight\fR.
.TP
.BI "\-f "time
.ns
.TP
.BI "\-\-from "time
Start saving pictures at the step or frame \fItime\fR.
.TP
.BI "\-t "time
.ns
.TP
.BI "\-\-to "time
Save pictures until the step or frame \fItime\fR.
.TP
.B \-\-animation
Tells the program to save animations instead of instructions.
.TP
.B \-\-instructions
Tells the program to save instructions instead of animations (default).
.TP
.B \-\-highlight
Adds a border around the pieces in the step or frame they appear when saving pictures.
.TP
.BI "\-l "path
.ns
.TP
.BI "\-\-libpath "path
Use alternate pieces library found in \fIpath \fR. The Debian version
of \fILeoCAD \fRuses /usr/share/leocad/ as the default.
.TP
.BI "\-\-libgl "library
Sets the name of the OpenGL dynamic library used by the program to \fIlibrary\fR.
The default library names searched are libGL.so and libMesaGL.so.
.SH ENVIRONMENT
``LEOCAD_LIB'' may be set to the path of the pieces library.
.SH EXAMPLES
.PP
$ leocad car.lcd -i car.png -w 640 -h 480
.PP
This will start LeoCAD, load the file "car.lcd," create a png called
"car.png" with a resolution of 640x480 and exit when done.
.SH FILES
.TP
.I ~/.leocad
User preferences file
.SH BUGS
If you find any bugs please report them at http://trac.gerf.org/leocad/
.SH AUTHOR
\fILeoCAD \fRis written by Leonardo Zide <leo@gerf.org>.
You can visit the LeoCAD homepage at http://www.leocad.org/
This manual page was written by Patrick Mahoney <pat7@gmx.net>,
for the Debian GNU/Linux system (but may be used by others).

21
fixwin.sh Executable file
View file

@ -0,0 +1,21 @@
#!/bin/sh
# script to rename all files to lowercase and remove ^M from them
for j in linux common ; do
for i in $j/*.cpp $j/*.c $j/*.h $j/*.mk ; do
if [ -f $i ]; then
echo $i | awk \
'{
if (tolower($1) != $1)
{
system ("mv "$1" "$1".lower")
system ("mv "$1".lower "tolower($1))
print $i
}
}'
grep -ls
$i | perl -pi -e 's/
$//g'
fi
done
done

11
generic.mk Normal file
View file

@ -0,0 +1,11 @@
all:
$(MAKE) -C .. $@
clean:
$(MAKE) -C .. $@
veryclean:
$(MAKE) -C .. $@
.PHONY: all clean veryclean

1
linux/Makefile Normal file
View file

@ -0,0 +1 @@
include ../generic.mk

247
linux/basewnd.cpp Normal file
View file

@ -0,0 +1,247 @@
//
// BaseWnd class implementation for Linux
//
#include <stdlib.h>
#include <gdk/gdkkeysyms.h>
#include "basewnd.h"
#include "dialogs.h"
#include "main.h"
BaseWnd::BaseWnd (BaseWnd *parent, int menu_count)
{
m_pMenuItems = new BaseMenuItem[menu_count];
memset(m_pMenuItems, 0, sizeof(BaseMenuItem[menu_count]));
m_pParent = parent;
m_pXID = gtk_window_new (GTK_WINDOW_TOPLEVEL);
if (parent != NULL)
gtk_window_set_transient_for (GTK_WINDOW (m_pXID), GTK_WINDOW (parent->GetXID ()));
}
BaseWnd::~BaseWnd ()
{
delete [] m_pMenuItems;
m_pMenuItems = NULL;
}
void BaseWnd::BeginWait ()
{
GdkCursor *cursor = gdk_cursor_new (GDK_WATCH);
gdk_window_set_cursor (m_pXID->window, cursor);
gdk_cursor_destroy (cursor);
}
void BaseWnd::EndWait ()
{
gdk_window_set_cursor (m_pXID->window, NULL);
}
int BaseWnd::MessageBox (const char* text, const char* caption, int flags)
{
GtkWidget *window, *w, *vbox, *hbox;
GtkAccelGroup *group;
int mode = (flags & LC_MB_TYPEMASK), ret, loop = 1;
guint tmp_key;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_signal_connect (GTK_OBJECT (window), "delete_event",
GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
// gtk_signal_connect (GTK_OBJECT (window), "destroy",
// GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL);
gtk_window_set_title (GTK_WINDOW (window), caption);
gtk_container_border_width (GTK_CONTAINER (window), 10);
gtk_object_set_data (GTK_OBJECT (window), "loop", &loop);
gtk_object_set_data (GTK_OBJECT (window), "ret", &ret);
if (m_pXID != NULL)
gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (m_pXID));
// gtk_widget_realize (window);
group = gtk_accel_group_new ();
gtk_window_add_accel_group (GTK_WINDOW (window), group);
vbox = gtk_vbox_new (FALSE, 10);
gtk_container_add (GTK_CONTAINER (window), vbox);
gtk_widget_show (vbox);
w = gtk_label_new (text);
gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2);
gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_LEFT);
gtk_widget_show (w);
w = gtk_hseparator_new ();
gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2);
gtk_widget_show (w);
hbox = gtk_hbox_new (FALSE, 10);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2);
gtk_widget_show (hbox);
if (mode == LC_MB_OK)
{
w = gtk_button_new_with_label ("Ok");
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (LC_OK));
GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
gtk_widget_grab_default (w);
gtk_widget_show (w);
ret = LC_OK;
}
else if (mode == LC_MB_OKCANCEL)
{
w = gtk_button_new_with_label ("Ok");
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (LC_OK));
GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
gtk_widget_grab_default (w);
gtk_widget_show (w);
w = gtk_button_new_with_label ("Cancel");
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (LC_CANCEL));
gtk_widget_show (w);
ret = LC_CANCEL;
}
else if (mode == LC_MB_YESNOCANCEL)
{
w = gtk_button_new_with_label ("");
tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (w)->child), "_Yes");
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (LC_YES));
gtk_widget_add_accelerator (w, "clicked", group, tmp_key, (GdkModifierType)0, (GtkAccelFlags)0);
GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
gtk_widget_grab_default (w);
gtk_widget_show (w);
gtk_window_set_focus (GTK_WINDOW (window), w);
w = gtk_button_new_with_label ("");
tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (w)->child), "_No");
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (LC_NO));
gtk_widget_add_accelerator (w, "clicked", group, tmp_key, (GdkModifierType)0, (GtkAccelFlags)0);
gtk_widget_show (w);
w = gtk_button_new_with_label ("");
tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (w)->child), "_Cancel");
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (LC_CANCEL));
gtk_widget_add_accelerator (w, "clicked", group, tmp_key, (GdkModifierType)0, (GtkAccelFlags)0);
gtk_widget_add_accelerator (w, "clicked", group, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
gtk_widget_show (w);
ret = LC_CANCEL;
}
else /* if (mode == LC_MB_YESNO) */
{
w = gtk_button_new_with_label ("Yes");
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (LC_YES));
GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
gtk_widget_grab_default (w);
gtk_widget_show (w);
w = gtk_button_new_with_label ("No");
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (LC_NO));
gtk_widget_show (w);
ret = LC_NO;
}
gtk_widget_show (window);
gtk_grab_add (window);
while (loop)
gtk_main_iteration ();
gtk_grab_remove (window);
gtk_widget_destroy (window);
return ret;
}
void BaseWnd::ShowMenuItem (int id, bool show)
{
if (!m_pMenuItems[id].widget)
return;
if (show)
gtk_widget_show (m_pMenuItems[id].widget);
else
gtk_widget_hide (m_pMenuItems[id].widget);
}
void BaseWnd::EnableMenuItem (int id, bool enable)
{
if (!m_pMenuItems[id].widget)
return;
gtk_widget_set_sensitive (m_pMenuItems[id].widget, enable);
}
void BaseWnd::CheckMenuItem (int id, bool check)
{
if (!m_pMenuItems[id].widget)
return;
ignore_commands = true;
gtk_check_menu_item_set_state (GTK_CHECK_MENU_ITEM (m_pMenuItems[id].widget), check);
ignore_commands = false;
}
#include <stdio.h>
void BaseWnd::SetMenuItemText (int id, const char *text)
{
gboolean underscore;
gchar *r;
const char *p;
gchar *pattern;
gint length;
if (!m_pMenuItems[id].widget)
return;
length = strlen (text);
pattern = g_new (gchar, length+1);
underscore = FALSE;
p = text;
r = pattern;
while (*p)
{
if (underscore)
{
if (*p == '&')
*r++ = *p;
else
{
*r++ = '_';
*r++ = *p;
}
underscore = FALSE;
}
else
{
if (*p == '&')
underscore = TRUE;
else
*r++ = *p;
}
p++;
}
*r = 0;
gtk_label_set_text_with_mnemonic(GTK_LABEL(GTK_BIN(m_pMenuItems[id].widget)->child), pattern);
g_free (pattern);
}

3193
linux/dialogs.cpp Normal file

File diff suppressed because it is too large Load diff

40
linux/dialogs.h Normal file
View file

@ -0,0 +1,40 @@
#ifndef _DIALOGS_H_
#define _DIALOGS_H_
// Dialog support functions
void dialog_button_callback (GtkWidget *widget, gpointer data);
gint dialog_delete_callback (GtkWidget *widget, GdkEvent* event, gpointer data);
// obsolete
int dlg_domodal (GtkWidget* dlg, int def);
void dlg_end (int ret);
gint dlg_delete_callback (GtkWidget *widget, GdkEvent* event, gpointer data);
void dlg_default_callback(GtkWidget *widget, gpointer data);
void modifydlg_toggle ();
// All dialogs
int openprojectdlg_execute (char* filename);
int saveprojectdlg_execute (char* filename);
int savepicturedlg_execute (void* param);
int msgbox_execute (const char* text, const char *caption, int flags);
int filedlg_execute(const char* caption, char* filename);
int arraydlg_execute(void* param);
int aboutdlg_execute(void* param);
int htmldlg_execute(void* param);
int imageoptsdlg_execute(void* param, bool from_htmldlg);
int povraydlg_execute(void* param);
int preferencesdlg_execute(void* param);
int propertiesdlg_execute(void* param);
int groupeditdlg_execute(void* param);
int groupdlg_execute(void* param);
int minifigdlg_execute(void* param);
int librarydlg_execute(void* param);
#endif // _DIALOGS_H_

345
linux/dlgfile.cpp Executable file
View file

@ -0,0 +1,345 @@
//
// This file holds all the dialogs that are called
// from the 'File' submenu:
//
// - File Open Dialog
// - File Save Dialog
// - Save Picture Dialog
// - Piece Library Manager
//
#include <gtk/gtk.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include "system.h"
#include "dialogs.h"
#include "file.h"
#include "image.h"
#include "main.h"
// =============================================================================
// Open Project Dialog
static void openprojectdlg_select (GtkCList *clist, gint row, gint col, GdkEvent *event, GtkPreview *preview)
{
GtkWidget *parent = gtk_widget_get_toplevel (GTK_WIDGET (clist));
const char *filename, *p;
bool loaded = false;
Image image;
filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (parent));
p = strrchr (filename, '.');
if ((p != NULL) && (g_strcasecmp (p+1, "lcd") == 0))
{
float fv;
char id[32];
FileDisk file;
file.Open (filename, "rb");
file.Read (id, 32);
sscanf (strchr(id, ' '), "%f", &fv);
if (fv > 0.4f)
{
file.Read(&fv, 4);
if (fv > 0.7f)
{
unsigned long dwPosition;
file.Seek (-4, SEEK_END);
file.Read (&dwPosition, 4);
file.Seek (dwPosition, SEEK_SET);
if (dwPosition != 0)
{
if (fv < 1.0f)
{
file.Seek (54, SEEK_CUR);
image.Allocate (120, 100, false);
file.Read (image.GetData (), 36000);
for (int y = 0; y < 50; y++)
for (int x = 0; x < 120; x++)
{
unsigned char *from = image.GetData() + x*3 + y*360;
unsigned char *to = image.GetData() + x*3 + (100-y-1)*360;
unsigned char tmp[3] = { from[0], from[1], from[2] };
from[0] = to[2];
from[1] = to[1];
from[2] = to[0];
to[0] = tmp[2];
to[1] = tmp[1];
to[2] = tmp[0];
}
loaded = true;
}
else
{
loaded = image.FileLoad (file);
}
}
}
}
file.Close();
}
if (loaded == false)
{
GtkWidget *w = GTK_WIDGET (preview);
guchar row[360];
for (int x = 0; x < 120; x++)
{
row[x*3] = w->style->bg[0].red/0xFF;
row[x*3+1] = w->style->bg[0].green/0xFF;
row[x*3+2] = w->style->bg[0].blue/0xFF;
}
for (int y = 0; y < 100; y++)
gtk_preview_draw_row (preview, row, 0, y, 120);
gtk_widget_draw (w, NULL);
}
else
{
for (int y = 0; y < 100; y++)
gtk_preview_draw_row (preview, image.GetData ()+y*360, 0, y, 120);
gtk_widget_draw (GTK_WIDGET (preview), NULL);
}
}
int openprojectdlg_execute (char* filename)
{
GtkWidget *dlg, *preview, *vbox, *frame, *frame2, *hbox;
int ret = LC_CANCEL, loop = 1, len;
dlg = gtk_file_selection_new ("Open Project");
gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (((GtkWidget*)(*main_window))));
gtk_signal_connect (GTK_OBJECT (dlg), "delete_event",
GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL);
gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (dlg)->ok_button), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (LC_OK));
gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (dlg)->cancel_button), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (LC_CANCEL));
gtk_object_set_data (GTK_OBJECT (dlg), "loop", &loop);
gtk_object_set_data (GTK_OBJECT (dlg), "ret", &ret);
// add preview support
hbox = GTK_FILE_SELECTION (dlg)->file_list->parent->parent;
vbox = gtk_vbox_new (FALSE, 0);
gtk_widget_show (vbox);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, TRUE, 0);
frame = gtk_frame_new ("Preview");
gtk_widget_show (frame);
gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, FALSE, 0);
frame2 = gtk_frame_new (NULL);
gtk_widget_show (frame2);
gtk_container_add (GTK_CONTAINER (frame), frame2);
gtk_container_border_width (GTK_CONTAINER (frame2), 5);
gtk_frame_set_shadow_type (GTK_FRAME (frame2), GTK_SHADOW_NONE);
preview = gtk_preview_new (GTK_PREVIEW_COLOR);
gtk_container_add (GTK_CONTAINER (frame2), preview);
gtk_preview_size (GTK_PREVIEW (preview), 120, 100);
gtk_widget_show (preview);
gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (dlg)->file_list), "select_row",
GTK_SIGNAL_FUNC (openprojectdlg_select), preview);
len = strlen (filename);
if (len != 0)
{
if (filename[len-1] != '/')
strcat (filename, "/");
gtk_file_selection_set_filename (GTK_FILE_SELECTION (dlg), filename);
}
gtk_widget_show (dlg);
gtk_grab_add (dlg);
while (loop)
gtk_main_iteration ();
if (ret == LC_OK)
strcpy (filename, gtk_file_selection_get_filename (GTK_FILE_SELECTION (dlg)));
gtk_grab_remove (dlg);
gtk_widget_destroy (dlg);
return ret;
}
// =============================================================================
// Save Project Dialog
static void saveprojectdlg_preview (GtkToggleButton *button, gpointer data)
{
Sys_ProfileSaveInt ("Default", "Save Preview", gtk_toggle_button_get_active (button));
}
// used by the save project and save picture dialogs
static void savefiledlg_ok (GtkWidget *widget, gpointer data)
{
GtkWidget *parent;
int *loop, *ret;
parent = gtk_widget_get_toplevel (widget);
loop = (int*)gtk_object_get_data (GTK_OBJECT (parent), "loop");
ret = (int*)gtk_object_get_data (GTK_OBJECT (parent), "ret");
if ((GPOINTER_TO_INT (data) == LC_OK) &&
(access (gtk_file_selection_get_filename (GTK_FILE_SELECTION (parent)), R_OK) == 0))
if (Sys_MessageBox ("File already exists, overwrite ?", "LeoCAD", LC_MB_YESNO) == LC_NO)
return;
*loop = 0;
*ret = GPOINTER_TO_INT (data);
}
int saveprojectdlg_execute (char* filename)
{
GtkWidget *dlg, *check;
int ret = LC_CANCEL, loop = 1;
dlg = gtk_file_selection_new ("Save Project");
gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (((GtkWidget*)(*main_window))));
gtk_signal_connect (GTK_OBJECT (dlg), "delete_event",
GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL);
gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (dlg)->ok_button), "clicked",
GTK_SIGNAL_FUNC (savefiledlg_ok), GINT_TO_POINTER (LC_OK));
gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (dlg)->cancel_button), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (LC_CANCEL));
gtk_object_set_data (GTK_OBJECT (dlg), "loop", &loop);
gtk_object_set_data (GTK_OBJECT (dlg), "ret", &ret);
// add preview checkbox
check = gtk_check_button_new_with_label ("Save Preview");
gtk_widget_show (check);
gtk_box_pack_start (GTK_BOX (GTK_FILE_SELECTION (dlg)->main_vbox), check, FALSE, FALSE, 0);
int i = Sys_ProfileLoadInt ("Default", "Save Preview", 0);
if (i != 0)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE);
gtk_signal_connect (GTK_OBJECT (check), "toggled", GTK_SIGNAL_FUNC (saveprojectdlg_preview), NULL);
gtk_file_selection_set_filename (GTK_FILE_SELECTION (dlg), filename);
gtk_widget_show (dlg);
gtk_grab_add (dlg);
while (loop)
gtk_main_iteration ();
if (ret == LC_OK)
strcpy (filename, gtk_file_selection_get_filename (GTK_FILE_SELECTION (dlg)));
gtk_grab_remove (dlg);
gtk_widget_destroy (dlg);
return ret;
}
// =============================================================================
// Save Picture Dialog
static void savepicturedlg_options (GtkWidget *widget, gpointer data)
{
imageoptsdlg_execute (data, false);
}
int savepicturedlg_execute (void* param)
{
GtkWidget *dlg, *button;
int ret = LC_CANCEL, loop = 1;
unsigned long image = Sys_ProfileLoadInt ("Default", "Image Options", 1|LC_IMAGE_TRANSPARENT);
LC_IMAGEDLG_OPTS* opts = (LC_IMAGEDLG_OPTS*)param;
opts->width = Sys_ProfileLoadInt ("Default", "Image Width", gdk_screen_width ());
opts->height = Sys_ProfileLoadInt ("Default", "Image Height", gdk_screen_height ());
opts->imopts.quality = Sys_ProfileLoadInt ("Default", "JPEG Quality", 70);
opts->imopts.interlaced = (image & LC_IMAGE_PROGRESSIVE) != 0;
opts->imopts.transparent = (image & LC_IMAGE_TRANSPARENT) != 0;
opts->imopts.truecolor = (image & LC_IMAGE_HIGHCOLOR) != 0;
opts->imopts.pause = (float)Sys_ProfileLoadInt ("Default", "AVI Pause", 100)/100;
opts->imopts.format = (unsigned char)(image & ~(LC_IMAGE_MASK));
dlg = gtk_file_selection_new ("Save Picture");
gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (((GtkWidget*)(*main_window))));
gtk_signal_connect (GTK_OBJECT (dlg), "delete_event",
GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL);
gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (dlg)->ok_button), "clicked",
GTK_SIGNAL_FUNC (savefiledlg_ok), GINT_TO_POINTER (LC_OK));
gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (dlg)->cancel_button), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (LC_CANCEL));
gtk_object_set_data (GTK_OBJECT (dlg), "loop", &loop);
gtk_object_set_data (GTK_OBJECT (dlg), "ret", &ret);
// add the options button
button = gtk_button_new_with_label ("Options");
gtk_widget_show (button);
gtk_box_pack_end (GTK_BOX (GTK_FILE_SELECTION (dlg)->ok_button->parent), button, TRUE, TRUE, 0);
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (savepicturedlg_options), param);
/*
gtk_file_selection_set_filename (GTK_FILE_SELECTION (dlg), filename);
*/
gtk_widget_show (dlg);
gtk_grab_add (dlg);
while (loop)
gtk_main_iteration ();
if (ret == LC_OK)
{
char ext[5], *p;
strcpy (opts->filename, gtk_file_selection_get_filename (GTK_FILE_SELECTION (dlg)));
if (strlen (opts->filename) == 0)
ret = LC_CANCEL;
p = strrchr (opts->filename, '.');
if (p != NULL)
{
strcpy (ext, p+1);
strlwr (ext);
}
else
ext[0] = '\0';
if ((strcmp (ext, "jpg") != 0) && (strcmp (ext, "jpeg") != 0) &&
(strcmp (ext, "bmp") != 0) && (strcmp (ext, "gif") != 0) &&
(strcmp (ext, "png") != 0) && (strcmp (ext, "avi") != 0))
{
switch (opts->imopts.format)
{
case LC_IMAGE_BMP: strcat(opts->filename, ".bmp"); break;
case LC_IMAGE_GIF: strcat(opts->filename, ".gif"); break;
case LC_IMAGE_JPG: strcat(opts->filename, ".jpg"); break;
case LC_IMAGE_PNG: strcat(opts->filename, ".png"); break;
case LC_IMAGE_AVI: strcat(opts->filename, ".avi"); break;
}
}
}
gtk_grab_remove (dlg);
gtk_widget_destroy (dlg);
return ret;
}
// =============================================================================
// Piece Library Manager

408
linux/dlgpiece.cpp Executable file
View file

@ -0,0 +1,408 @@
//
// This file holds all the dialogs that are called
// from the 'Pieces' submenu:
//
// - Group Name
// - Edit Groups
// - Minifig Wizard
//
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "opengl.h"
#include "gtktools.h"
#include "system.h"
#include "typedefs.h"
#include "globals.h"
#include "dialogs.h"
#include "matrix.h"
#include "pieceinf.h"
#include "main.h"
#include "minifig.h"
// =========================================================
// Minifig Wizard
typedef struct
{
MinifigWizard* wizard;
GtkWidget *pieces[LC_MFW_NUMITEMS];
GtkWidget *colors[LC_MFW_NUMITEMS];
GtkWidget *angles[LC_MFW_NUMITEMS];
GtkWidget *preview;
GtkWidget *combo;
} LC_MINIFIGDLG_STRUCT;
// User wants to add the minifig to the project
static void minifigdlg_ok(GtkWidget *widget, gpointer data)
{
// LC_MINIFIGDLG_STRUCT* s = (LC_MINIFIGDLG_STRUCT*)data;
// LC_MINIFIGDLG_OPTS* opts = (LC_MINIFIGDLG_OPTS*)s->data;
dlg_end (LC_OK);
}
// A new color was selected from the menu
static void minifigdlg_color_response (GtkWidget *widget, gpointer data)
{
LC_MINIFIGDLG_STRUCT* info;
GtkWidget* button;
int i;
button = (GtkWidget*)gtk_object_get_data (GTK_OBJECT (widget), "button");
info = (LC_MINIFIGDLG_STRUCT*)gtk_object_get_data (GTK_OBJECT (button), "info");
if (!info)
return;
for (i = 0; i < 15; i++)
if (info->colors[i] == button)
break;
info->wizard->SetColor (i, GPOINTER_TO_INT (data));
info->wizard->Redraw ();
set_button_pixmap2 (button, FlatColorArray[GPOINTER_TO_INT(data)]);
}
// A color button was clicked
static void minifigdlg_color_clicked (GtkWidget *widget, gpointer data)
{
int i;
GtkWidget *menu, *menuitem;
menu = gtk_menu_new ();
for (i = 0; i < LC_MAXCOLORS; i++)
{
menuitem = gtk_menu_item_new_with_label (colornames[i]);
gtk_widget_show (menuitem);
gtk_menu_append (GTK_MENU (menu), menuitem);
gtk_object_set_data (GTK_OBJECT (menuitem), "button", widget);
gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
GTK_SIGNAL_FUNC (minifigdlg_color_response), (void*)i);
}
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, 0);
}
// New piece was selected
static void minifigdlg_piece_changed (GtkWidget *widget, gpointer data)
{
LC_MINIFIGDLG_STRUCT* info;
int i, piece_type = -1, piece_index = -1;
const gchar* desc;
info = (LC_MINIFIGDLG_STRUCT*)gtk_object_get_data (GTK_OBJECT (widget), "info");
if (info == NULL)
return;
for (i = 0; i < LC_MFW_NUMITEMS; i++)
if (GTK_COMBO (info->pieces[i])->entry == widget)
{
piece_type = i;
break;
}
ObjArray<lcMinifigPieceInfo>& InfoArray = info->wizard->mSettings[piece_type];
desc = gtk_entry_get_text (GTK_ENTRY (widget));
for (i = 0; i < InfoArray.GetSize(); i++)
{
if (!strcmp(InfoArray[i].Description, desc))
{
piece_index = i;
break;
}
}
if (piece_index == -1 || piece_type == -1)
return;
info->wizard->SetSelectionIndex (piece_type, piece_index);
info->wizard->Redraw ();
}
static void minifigdlg_updatecombo (LC_MINIFIGDLG_STRUCT* s)
{
char **names;
int count;
GList *lst = NULL;
s->wizard->GetMinifigNames (&names, &count);
for (int i = 0; i < count; i++)
lst = g_list_append (lst, names[i]);
if (lst == NULL)
lst = g_list_append (lst, (void*)"");
gtk_combo_set_popdown_strings (GTK_COMBO (s->combo), lst);
g_list_free (lst);
}
static void minifigdlg_updateselection (LC_MINIFIGDLG_STRUCT* s)
{
for (int i = 0; i < LC_MFW_NUMITEMS; i++)
{
int index = s->wizard->GetSelectionIndex(i);
gtk_signal_handler_block_by_func (GTK_OBJECT (GTK_COMBO (s->pieces[i])->entry),
GTK_SIGNAL_FUNC (minifigdlg_piece_changed), NULL);
gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (s->pieces[i])->entry), s->wizard->mSettings[i][index].Description);
gtk_list_select_item (GTK_LIST (GTK_COMBO (s->pieces[i])->list), index);
gtk_signal_handler_unblock_by_func (GTK_OBJECT (GTK_COMBO (s->pieces[i])->entry),
GTK_SIGNAL_FUNC (minifigdlg_piece_changed), NULL);
}
}
static void minifigdlg_load (GtkWidget *widget, gpointer data)
{
LC_MINIFIGDLG_STRUCT* s = (LC_MINIFIGDLG_STRUCT*)data;
if (s->wizard->LoadMinifig (gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (s->combo)->entry))) == false)
return;
for (int i = 0; i < LC_MFW_NUMITEMS; i++)
{
set_button_pixmap2 (s->colors[i], FlatColorArray[s->wizard->m_Colors[i]]);
if (s->angles[i] != NULL)
gtk_spin_button_set_value (GTK_SPIN_BUTTON (s->angles[i]), s->wizard->m_Angles[i]);
}
minifigdlg_updateselection (s);
s->wizard->Redraw ();
}
static void minifigdlg_save (GtkWidget *widget, gpointer data)
{
LC_MINIFIGDLG_STRUCT* s = (LC_MINIFIGDLG_STRUCT*)data;
s->wizard->SaveMinifig (gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (s->combo)->entry)));
minifigdlg_updatecombo (s);
}
static void minifigdlg_delete (GtkWidget *widget, gpointer data)
{
LC_MINIFIGDLG_STRUCT* s = (LC_MINIFIGDLG_STRUCT*)data;
s->wizard->DeleteMinifig (gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (s->combo)->entry)));
minifigdlg_updatecombo (s);
}
static void adj_changed (GtkAdjustment *adj, gpointer data)
{
LC_MINIFIGDLG_STRUCT* info = (LC_MINIFIGDLG_STRUCT*)data;
float val;
int i;
for (i = 0; i < LC_MFW_NUMITEMS; i++)
if (info->angles[i] != NULL)
if (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (info->angles[i])) == adj)
break;
if (i == LC_MFW_NUMITEMS)
return;
val = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (info->angles[i]));
if (val == info->wizard->m_Angles[i])
return;
info->wizard->SetAngle (i, val);
if (info->preview != NULL)
info->wizard->Redraw ();
}
// Create a combo box with a color selection control
static void minifigdlg_createpair (LC_MINIFIGDLG_STRUCT* info, int idx, int num, GtkWidget* table)
{
GtkWidget *combo, *color, *spin;
GtkObject *adj;
combo = info->pieces[num] = gtk_combo_new ();
gtk_widget_show (combo);
gtk_table_attach (GTK_TABLE (table), combo, 0, 1, idx, idx+1,
(GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) GTK_EXPAND, 0, 0);
gtk_widget_set_usize (combo, 60, 25);
gtk_entry_set_editable (GTK_ENTRY (GTK_COMBO (combo)->entry), FALSE);
gtk_signal_connect (GTK_OBJECT (GTK_COMBO (combo)->entry), "changed",
GTK_SIGNAL_FUNC (minifigdlg_piece_changed), NULL);
gtk_object_set_data (GTK_OBJECT (GTK_COMBO (combo)->entry), "info", info);
color = info->colors[num] = gtk_button_new_with_label ("");
gtk_widget_set_events (color, GDK_EXPOSURE_MASK);
gtk_widget_show (color);
gtk_object_set_data (GTK_OBJECT (color), "color", &info->wizard->m_Colors[num]);
gtk_object_set_data (GTK_OBJECT (color), "info", info);
gtk_widget_set_usize (color, 40, 25);
gtk_signal_connect (GTK_OBJECT (color), "clicked",
GTK_SIGNAL_FUNC (minifigdlg_color_clicked), info);
gtk_table_attach (GTK_TABLE (table), color, 1, 2, idx, idx+1,
(GtkAttachOptions) GTK_FILL, (GtkAttachOptions) GTK_EXPAND, 0, 0);
if ((num == LC_MFW_TORSO) || (num == LC_MFW_HIPS))
{
info->angles[num] = NULL;
return;
}
adj = gtk_adjustment_new (0, -180, 180, 1, 10, 10);
gtk_signal_connect (adj, "value_changed", GTK_SIGNAL_FUNC (adj_changed), info);
spin = info->angles[num] = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1, 0);
gtk_widget_show (spin);
gtk_object_set_data (GTK_OBJECT (color), "info", info);
// gtk_widget_set_usize (spin, 40, -1);
gtk_table_attach (GTK_TABLE (table), spin, 2, 3, idx, idx+1,
(GtkAttachOptions) GTK_FILL, (GtkAttachOptions) GTK_EXPAND, 0, 0);
gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spin), TRUE);
}
int minifigdlg_execute (void* param)
{
GtkWidget *vbox, *hbox, *frame, *table;
GtkWidget *dlg, *button;
LC_MINIFIGDLG_STRUCT s;
int i;
memset (&s, 0, sizeof (s));
s.wizard = (MinifigWizard*)param;
dlg = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (((GtkWidget*)(*main_window))));
gtk_signal_connect (GTK_OBJECT (dlg), "delete_event",
GTK_SIGNAL_FUNC (dlg_delete_callback), NULL);
gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL);
gtk_widget_set_usize (dlg, 600, 360);
gtk_window_set_title (GTK_WINDOW (dlg), "Minifig Wizard");
// gtk_window_set_policy (GTK_WINDOW (dlg), FALSE, FALSE, FALSE);
gtk_widget_realize (dlg);
vbox = gtk_vbox_new (FALSE, 10);
gtk_widget_show (vbox);
gtk_container_add (GTK_CONTAINER (dlg), vbox);
gtk_container_border_width (GTK_CONTAINER (vbox), 5);
hbox = gtk_hbox_new (FALSE, 5);
gtk_widget_show (hbox);
gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
table = gtk_table_new (8, 3, FALSE);
gtk_widget_show (table);
gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0);
gtk_table_set_col_spacings (GTK_TABLE (table), 5);
gtk_table_set_row_spacings (GTK_TABLE (table), 5);
minifigdlg_createpair (&s, 0, LC_MFW_HAT, table);
minifigdlg_createpair (&s, 1, LC_MFW_NECK, table);
minifigdlg_createpair (&s, 2, LC_MFW_RIGHT_ARM, table);
minifigdlg_createpair (&s, 3, LC_MFW_RIGHT_HAND, table);
minifigdlg_createpair (&s, 4, LC_MFW_RIGHT_TOOL, table);
minifigdlg_createpair (&s, 5, LC_MFW_HIPS, table);
minifigdlg_createpair (&s, 6, LC_MFW_RIGHT_LEG, table);
minifigdlg_createpair (&s, 7, LC_MFW_RIGHT_SHOE, table);
s.wizard->Create (&s.preview);
frame = gtk_frame_new (NULL);
gtk_widget_show (frame);
gtk_container_add (GTK_CONTAINER (hbox), frame);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_widget_set_usize (GTK_WIDGET (s.preview), 150, 300);
gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (s.preview));
gtk_widget_show (GTK_WIDGET (s.preview));
gtk_object_set_data (GTK_OBJECT (s.preview), "minifig", &s);
table = gtk_table_new (7, 3, FALSE);
gtk_widget_show (table);
gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0);
gtk_table_set_col_spacings (GTK_TABLE (table), 5);
gtk_table_set_row_spacings (GTK_TABLE (table), 5);
minifigdlg_createpair (&s, 0, LC_MFW_HEAD, table);
minifigdlg_createpair (&s, 1, LC_MFW_TORSO, table);
minifigdlg_createpair (&s, 2, LC_MFW_LEFT_ARM, table);
minifigdlg_createpair (&s, 3, LC_MFW_LEFT_HAND, table);
minifigdlg_createpair (&s, 4, LC_MFW_LEFT_TOOL, table);
minifigdlg_createpair (&s, 5, LC_MFW_LEFT_LEG, table);
minifigdlg_createpair (&s, 6, LC_MFW_LEFT_SHOE, table);
hbox = gtk_hbox_new (FALSE, 10);
gtk_widget_show (hbox);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
s.combo = gtk_combo_new ();
gtk_widget_show (s.combo);
gtk_box_pack_start (GTK_BOX (hbox), s.combo, FALSE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (GTK_COMBO (s.combo)->entry), "changed",
GTK_SIGNAL_FUNC (minifigdlg_load), &s);
button = gtk_button_new_with_label ("Save");
gtk_signal_connect (GTK_OBJECT (button), "clicked",
GTK_SIGNAL_FUNC (minifigdlg_save), &s);
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
gtk_widget_set_usize (button, 70, 25);
button = gtk_button_new_with_label ("Delete");
gtk_signal_connect (GTK_OBJECT (button), "clicked",
GTK_SIGNAL_FUNC (minifigdlg_delete), &s);
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
gtk_widget_set_usize (button, 70, 25);
button = gtk_button_new_with_label ("OK");
gtk_signal_connect (GTK_OBJECT (button), "clicked",
GTK_SIGNAL_FUNC (minifigdlg_ok), &s);
gtk_widget_show (button);
gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, TRUE, 0);
gtk_widget_set_usize (button, 70, 25);
GtkAccelGroup *accel_group = gtk_accel_group_new ();
gtk_window_add_accel_group (GTK_WINDOW (dlg), accel_group);
gtk_widget_add_accelerator (button, "clicked", accel_group,
GDK_Return, (GdkModifierType)0, GTK_ACCEL_VISIBLE);
button = gtk_button_new_with_label ("Cancel");
gtk_signal_connect (GTK_OBJECT (button), "clicked",
GTK_SIGNAL_FUNC (dlg_default_callback), GINT_TO_POINTER (LC_CANCEL));
gtk_widget_show (button);
gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, TRUE, 0);
gtk_widget_set_usize (button, 70, 25);
gtk_widget_add_accelerator (button, "clicked", accel_group,
GDK_Escape, (GdkModifierType)0, GTK_ACCEL_VISIBLE);
// Fill the combo boxes with the available pieces
for (i = 0; i < LC_MFW_NUMITEMS; i++)
{
GList* names = NULL;
int count = s.wizard->mSettings[i].GetSize();
for (int j = 0; j < count; j++)
names = g_list_append (names, s.wizard->mSettings[i][j].Description);
if (names != NULL)
{
gtk_signal_handler_block_by_func(GTK_OBJECT(GTK_COMBO(s.pieces[i])->entry),
GTK_SIGNAL_FUNC(minifigdlg_piece_changed), NULL);
gtk_combo_set_popdown_strings (GTK_COMBO (s.pieces[i]), names);
g_list_free (names);
gtk_signal_handler_unblock_by_func(GTK_OBJECT(GTK_COMBO(s.pieces[i])->entry),
GTK_SIGNAL_FUNC(minifigdlg_piece_changed), NULL);
}
}
minifigdlg_updatecombo (&s);
minifigdlg_updateselection (&s);
gtk_widget_show(dlg);
for (i = 0; i < LC_MFW_NUMITEMS; i++)
{
set_button_pixmap2(s.colors[i], FlatColorArray[s.wizard->m_Colors[i]]);
}
return dlg_domodal(dlg, LC_CANCEL);
}

280
linux/glwindow.cpp Normal file
View file

@ -0,0 +1,280 @@
//
// OpenGL window
//
#include <stdio.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include "opengl.h"
#include "glwindow.h"
typedef struct
{
GtkWidget *widget;
Display* xdisplay;
GLXContext context;
} GLWindowPrivate;
// =============================================================================
// static functions
static gint realize_event (GtkWidget *widget, gpointer data)
{
GLWindow *wnd = (GLWindow*)data;
wnd->OnInitialUpdate ();
return TRUE;
}
static gint expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
GLWindow *wnd = (GLWindow*)data;
if (event->count > 0)
return TRUE;
wnd->OnDraw ();
return TRUE;
}
static gint button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer data)
{
GLWindow *wnd = (GLWindow*)data;
int x, y;
x = (int)event->x;
y = widget->allocation.height - (int)event->y - 1;
if (event->type == GDK_BUTTON_PRESS)
{
if (event->button == 1)
wnd->OnLeftButtonDown (x, y, (event->state & GDK_CONTROL_MASK) != 0,
(event->state & GDK_SHIFT_MASK) != 0);
else if (event->button == 3)
wnd->OnRightButtonDown (x, y, (event->state & GDK_CONTROL_MASK) != 0,
(event->state & GDK_SHIFT_MASK) != 0);
}
else if (event->type == GDK_2BUTTON_PRESS)
{
wnd->OnLeftButtonDoubleClick (x, y, (event->state & GDK_CONTROL_MASK) != 0,
(event->state & GDK_SHIFT_MASK) != 0);
}
gtk_window_set_focus (GTK_WINDOW (gtk_widget_get_toplevel (widget)), widget);
gdk_pointer_grab (widget->window, FALSE, (GdkEventMask)(GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|
GDK_POINTER_MOTION_MASK|GDK_POINTER_MOTION_HINT_MASK),
NULL, NULL, GDK_CURRENT_TIME);
return TRUE;
}
static gint button_release_event (GtkWidget *widget, GdkEventButton *event, gpointer data)
{
GLWindow *wnd = (GLWindow*)data;
int x, y;
x = (int)event->x;
y = widget->allocation.height - (int)event->y - 1;
gdk_pointer_ungrab (GDK_CURRENT_TIME);
if (event->button == 1)
wnd->OnLeftButtonUp (x, y, (event->state & GDK_CONTROL_MASK) != 0,
(event->state & GDK_SHIFT_MASK) != 0);
else if (event->button == 3)
wnd->OnRightButtonUp (x, y, (event->state & GDK_CONTROL_MASK) != 0,
(event->state & GDK_SHIFT_MASK) != 0);
return TRUE;
}
static gint pointer_motion_event (GtkWidget *widget, GdkEventMotion *event, gpointer data)
{
GLWindow *wnd = (GLWindow*)data;
GdkModifierType state;
int x, y;
if (event->is_hint)
{
gdk_window_get_pointer (event->window, &x, &y, &state);
state = (GdkModifierType)0;
}
else
{
x = (int)event->x;
y = (int)event->y;
state = (GdkModifierType)event->state;
}
y = widget->allocation.height - y - 1;
wnd->OnMouseMove (x, y, (event->state & GDK_CONTROL_MASK) != 0, (event->state & GDK_SHIFT_MASK) != 0);
return TRUE;
}
static gint size_allocate_event (GtkWidget *widget, GtkAllocation *allocation, gpointer data)
{
GLWindow *wnd = (GLWindow*)data;
wnd->OnSize (allocation->width, allocation->height);
return TRUE;
}
/*
static void destroy_event (GtkWidget *widget, gpointer data)
{
GLWindow *wnd = (GLWindow*)data;
wnd->DestroyContext ();
}
*/
// =============================================================================
// GLWindow class
GLWindow::GLWindow (GLWindow *share)
{
m_pShare = share;
m_pData = g_malloc (sizeof (GLWindowPrivate));
}
GLWindow::~GLWindow ()
{
DestroyContext ();
g_free (m_pData);
}
bool GLWindow::Create (void *data)
{
int attrlist[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 16, 0 };
GLWindowPrivate *prv = (GLWindowPrivate*)m_pData;
Display *dpy = GDK_DISPLAY();
GdkVisual *visual;
XVisualInfo *vi;
// choose visual
visual = gdk_visual_get_system ();
if (visual->depth < 16)
printf ("OpenGL fatal error: LeoCAD needs a display with at least 16 bit colors.\n");
if (dpy == NULL)
{
printf ("OpenGL fatal error: Cannot get display.\n");
return false;
}
prv->xdisplay = dpy;
vi = pfnglXChooseVisual (dpy, DefaultScreen (dpy), attrlist);
if (vi == NULL)
{
printf ("OpenGL fatal error: glXChooseVisual failed.\n");
return false;
}
visual = gdkx_visual_get (vi->visualid);
if (visual == NULL)
{
printf ("OpenGL fatal error: Cannot get visual.\n");
return false;
}
gtk_widget_push_colormap (gdk_colormap_new (visual, TRUE));
gtk_widget_push_visual (visual);
prv->widget = gtk_drawing_area_new ();
gtk_widget_set_double_buffered(GTK_WIDGET(prv->widget), FALSE);
if (m_pShare == NULL)
prv->context = pfnglXCreateContext (dpy, vi, NULL, True);
else
{
GLWindowPrivate *share = (GLWindowPrivate*)m_pShare->m_pData;
prv->context = pfnglXCreateContext (dpy, vi, share->context, True);
}
gtk_widget_pop_visual ();
gtk_widget_pop_colormap ();
XFree (vi);
GTK_WIDGET_SET_FLAGS (prv->widget, GTK_CAN_FOCUS);
gtk_widget_set_events (GTK_WIDGET (prv->widget), GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
// Connect signal handlers
gtk_signal_connect (GTK_OBJECT (prv->widget), "expose_event",
GTK_SIGNAL_FUNC (expose_event), this);
// gtk_signal_connect (GTK_OBJECT (prv->widget), "destroy",
// GTK_SIGNAL_FUNC (destroy_event), this);
gtk_signal_connect (GTK_OBJECT (prv->widget), "size_allocate",
GTK_SIGNAL_FUNC (size_allocate_event), this);
gtk_signal_connect (GTK_OBJECT (prv->widget), "motion_notify_event",
GTK_SIGNAL_FUNC (pointer_motion_event), this);
gtk_signal_connect (GTK_OBJECT (prv->widget), "button_press_event",
GTK_SIGNAL_FUNC (button_press_event), this);
gtk_signal_connect (GTK_OBJECT (prv->widget), "button_release_event",
GTK_SIGNAL_FUNC (button_release_event), this);
gtk_signal_connect (GTK_OBJECT (prv->widget), "realize",
GTK_SIGNAL_FUNC (realize_event), this);
*((GtkWidget**)data) = prv->widget;
return true;
}
void GLWindow::DestroyContext ()
{
GLWindowPrivate *prv = (GLWindowPrivate*)m_pData;
if (prv->context == pfnglXGetCurrentContext ())
pfnglXMakeCurrent (prv->xdisplay, None, NULL);
if (prv->context)
pfnglXDestroyContext (prv->xdisplay, prv->context);
prv->context = NULL;
}
void GLWindow::OnInitialUpdate ()
{
MakeCurrent ();
GL_InitializeExtensions ();
}
bool GLWindow::MakeCurrent ()
{
GLWindowPrivate *prv = (GLWindowPrivate*)m_pData;
gboolean ret = false;
if (prv->context)
ret = pfnglXMakeCurrent (prv->xdisplay, GDK_WINDOW_XWINDOW (prv->widget->window), prv->context);
return ret;
}
void GLWindow::SwapBuffers ()
{
GLWindowPrivate *prv = (GLWindowPrivate*)m_pData;
if (prv->context)
pfnglXSwapBuffers (GDK_WINDOW_XDISPLAY (prv->widget->window), GDK_WINDOW_XWINDOW (prv->widget->window));
}
void GLWindow::Redraw ()
{
GLWindowPrivate *prv = (GLWindowPrivate*)m_pData;
gtk_widget_draw (prv->widget, (GdkRectangle*)NULL);
}
void GLWindow::CaptureMouse()
{
}
void GLWindow::ReleaseMouse()
{
}

229
linux/gtkmisc.cpp Executable file
View file

@ -0,0 +1,229 @@
//
// Small functions to help with GTK
//
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <string.h>
#include "gtkmisc.h"
#include "globals.h"
#include "project.h"
//#include "pixmenu.h"
#include "gtktools.h"
// =============================================================================
// Pixmap functions
#include "pixmaps/vports01.xpm"
#include "pixmaps/vports02.xpm"
#include "pixmaps/vports03.xpm"
#include "pixmaps/vports04.xpm"
#include "pixmaps/vports05.xpm"
#include "pixmaps/vports06.xpm"
#include "pixmaps/vports07.xpm"
#include "pixmaps/vports08.xpm"
#include "pixmaps/vports09.xpm"
#include "pixmaps/vports10.xpm"
#include "pixmaps/vports11.xpm"
#include "pixmaps/vports12.xpm"
#include "pixmaps/vports13.xpm"
#include "pixmaps/vports14.xpm"
// Load a pixmap file from the disk
void load_pixmap (const char* filename, GdkPixmap **gdkpixmap, GdkBitmap **mask)
{
struct { const char* name; const char** data; } table[14] =
{
{ "vports01.xpm", vports01 },
{ "vports02.xpm", vports02 },
{ "vports03.xpm", vports03 },
{ "vports04.xpm", vports04 },
{ "vports05.xpm", vports05 },
{ "vports06.xpm", vports06 },
{ "vports07.xpm", vports07 },
{ "vports08.xpm", vports08 },
{ "vports09.xpm", vports09 },
{ "vports10.xpm", vports10 },
{ "vports11.xpm", vports11 },
{ "vports12.xpm", vports12 },
{ "vports13.xpm", vports13 },
{ "vports14.xpm", vports14 },
};
*gdkpixmap = NULL;
for (int i = 0; i < 14; i++)
if (strcmp (table[i].name, filename) == 0)
{
*gdkpixmap = gdk_pixmap_create_from_xpm_d (GDK_ROOT_PARENT(), mask, NULL, (gchar**)table[i].data);
break;
}
if (*gdkpixmap == NULL)
{
const char *dummy[] = { "1 1 1 1", " c None", " " };
*gdkpixmap = gdk_pixmap_create_from_xpm_d (GDK_ROOT_PARENT(), mask, NULL, (gchar**)dummy);
}
}
// Load a xpm file and return a pixmap widget
GtkWidget* create_pixmap (const char* filename)
{
GdkPixmap *gdkpixmap;
GdkBitmap *mask;
GtkWidget *pixmap;
load_pixmap (filename, &gdkpixmap, &mask);
pixmap = gtk_pixmap_new (gdkpixmap, mask);
gtk_widget_show (pixmap);
gdk_pixmap_unref (gdkpixmap);
gdk_pixmap_unref (mask);
return pixmap;
}
// =============================================================================
// Menu stuff
GtkWidget* menu_separator (GtkWidget *menu)
{
GtkWidget *menu_item = gtk_menu_item_new ();
gtk_menu_append (GTK_MENU (menu), menu_item);
gtk_widget_set_sensitive (menu_item, FALSE);
gtk_widget_show (menu_item);
return menu_item;
}
GtkWidget* menu_tearoff (GtkWidget *menu)
{
GtkWidget *menu_item = gtk_tearoff_menu_item_new ();
gtk_menu_append (GTK_MENU (menu), menu_item);
// gtk_widget_set_sensitive (menu_item, FALSE);
gtk_widget_show (menu_item);
return menu_item;
}
GtkWidget* create_sub_menu(GtkWidget* bar, const char* label, GtkAccelGroup* accel)
{
GtkWidget *item, *menu;
item = gtk_menu_item_new_with_mnemonic(label);
gtk_widget_show (item);
gtk_container_add (GTK_CONTAINER (bar), item);
menu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
return menu;
}
GtkWidget* create_menu_in_menu(GtkWidget* menu, const char* label, GtkAccelGroup* accel)
{
GtkWidget *item, *submenu;
item = gtk_menu_item_new_with_mnemonic(label);
gtk_widget_show (item);
gtk_container_add (GTK_CONTAINER (menu), item);
submenu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
return submenu;
}
GtkWidget* create_menu_item(GtkWidget *menu, const char *label, GtkAccelGroup *menu_accel,
GtkSignalFunc func, GtkObject *window, int id, const char* data)
{
GtkWidget *item;
item = gtk_menu_item_new_with_mnemonic (label);
gtk_widget_show (item);
gtk_container_add (GTK_CONTAINER (menu), item);
gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (func), GINT_TO_POINTER (id));
if (data != NULL)
gtk_object_set_data (window, data, item);
return item;
}
GtkWidget* create_pixmap_menu_item(GtkWidget *menu, const gchar *label, const char **pixmap, GtkAccelGroup *menu_accel,
GtkSignalFunc func, GtkObject *window, int id, const char* data)
{
GtkWidget *item, *pixmap_widget;
item = gtk_image_menu_item_new_with_mnemonic(label);
pixmap_widget = new_pixmap (GTK_WIDGET (window), pixmap);
gtk_widget_show (pixmap_widget);
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), pixmap_widget);
gtk_widget_show (item);
gtk_container_add (GTK_CONTAINER (menu), item);
gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (func), GINT_TO_POINTER (id));
if (data != NULL)
gtk_object_set_data (window, data, item);
return item;
}
GtkWidget* create_check_menu_item(GtkWidget *menu, const char *label, GtkAccelGroup *menu_accel,
GtkSignalFunc func, GtkObject *window, int id, const char* data)
{
GtkWidget *item;
item = gtk_check_menu_item_new_with_mnemonic(label);
gtk_widget_show (item);
gtk_container_add (GTK_CONTAINER (menu), item);
gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (func), GINT_TO_POINTER (id));
if (data != NULL)
gtk_object_set_data (window, data, item);
return item;
}
GtkWidget* create_radio_menu_item(GtkWidget *menu, GtkWidget *last, const char *label,
GtkAccelGroup *menu_accel, GtkSignalFunc func,
GtkObject *window, int id, const char* data)
{
GtkWidget *item;
GSList *group = NULL;
if (last != NULL)
group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (last));
item = gtk_radio_menu_item_new_with_mnemonic(group, label);
gtk_widget_show (item);
gtk_container_add (GTK_CONTAINER (menu), item);
gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (func), GINT_TO_POINTER (id));
if (data != NULL)
gtk_object_set_data (window, data, item);
return item;
}
GtkWidget* create_radio_menu_pixmap(GtkWidget *menu, GtkWidget *last, const char *filename,
GtkAccelGroup *menu_accel, GtkSignalFunc func,
GtkObject *window, int id, const char* data)
{
GtkWidget *item, *pixmap;
GSList *group = NULL;
if (last != NULL)
group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (last));
item = gtk_radio_menu_item_new (group);
gtk_widget_show (item);
gtk_container_add (GTK_CONTAINER (menu), item);
gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (func), GINT_TO_POINTER (id));
pixmap = create_pixmap (filename);
gtk_container_add (GTK_CONTAINER (item), pixmap);
if (data != NULL)
gtk_object_set_data (window, data, item);
return item;
}

25
linux/gtkmisc.h Executable file
View file

@ -0,0 +1,25 @@
#ifndef _GTK_MISC_H_
#define _GTK_MISC_H_
GtkWidget* create_pixmap (char* filename);
void load_pixmap (const char* filename, GdkPixmap **gdkpixmap, GdkBitmap **mask);
GtkWidget* menu_separator (GtkWidget *menu);
GtkWidget* menu_tearoff (GtkWidget *menu);
GtkWidget* create_sub_menu(GtkWidget *bar, const char *label, GtkAccelGroup *accel);
GtkWidget* create_menu_in_menu(GtkWidget *menu, const char *label, GtkAccelGroup *accel);
GtkWidget* create_menu_item(GtkWidget *menu, const char *label, GtkAccelGroup *accel,
GtkSignalFunc func, GtkObject *window, int id, const char* data);
GtkWidget* create_pixmap_menu_item(GtkWidget *menu, const char *label, const char **pixmap, GtkAccelGroup *accel,
GtkSignalFunc func, GtkObject *window, int id, const char* data);
GtkWidget* create_check_menu_item(GtkWidget *menu, const char *label, GtkAccelGroup *menu_accel,
GtkSignalFunc func, GtkObject *window, int id, const char* data);
GtkWidget* create_radio_menu_item(GtkWidget *menu, GtkWidget *last, const char *label,
GtkAccelGroup *menu_accel, GtkSignalFunc func,
GtkObject *window, int id, const char* data);
GtkWidget* create_radio_menu_pixmap(GtkWidget *menu, GtkWidget *last, const char *filename,
GtkAccelGroup *menu_accel, GtkSignalFunc func,
GtkObject *window, int id, const char* data);
#endif // _GTKMISC_H_

114
linux/gtktools.cpp Normal file
View file

@ -0,0 +1,114 @@
// Helper functions for GTK
//
#include <gtk/gtk.h>
#include "gtktools.h"
GtkWidget* new_pixmap(GtkWidget *widget, const char **data)
{
GdkPixmap *gdkpixmap;
GdkBitmap *mask;
GtkWidget *pixmap;
gdkpixmap = gdk_pixmap_create_from_xpm_d(widget->window, &mask, &widget->style->bg[GTK_STATE_NORMAL], (gchar**)data);
pixmap = gtk_pixmap_new (gdkpixmap, mask);
gdk_pixmap_unref (gdkpixmap);
gdk_pixmap_unref (mask);
return pixmap;
}
GtkWidget* clist_title_with_arrow(GtkWidget* clist, char col, const char* label_text)
{
GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
GtkWidget *arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_IN);
GtkWidget *label = gtk_label_new (label_text);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
gtk_box_pack_end (GTK_BOX (hbox), arrow, FALSE, TRUE, 0);
gtk_widget_show (label);
gtk_widget_show (hbox);
gtk_clist_set_column_widget (GTK_CLIST (clist), col, hbox);
return arrow;
}
void set_notebook_tab (GtkWidget *notebook, gint page_num, GtkWidget *widget)
{
gtk_notebook_set_tab_label(GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num), widget);
/*
GtkNotebookPage *page;
GtkWidget *notebook_page;
page = (GtkNotebookPage*) g_list_nth (GTK_NOTEBOOK (notebook)->children, page_num)->data;
notebook_page = page->child;
gtk_widget_ref (notebook_page);
gtk_notebook_remove_page (GTK_NOTEBOOK (notebook), page_num);
gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), notebook_page,
widget, page_num);
gtk_widget_unref (notebook_page);
*/
}
void set_button_pixmap (GtkWidget* widget, float* color)
{
if (widget->window == NULL)
return;
GdkColor c;
GdkGC* gc = gdk_gc_new(widget->window);
GdkPixmap* pixmap = gdk_pixmap_new(widget->window, widget->allocation.width - 20,
widget->allocation.height - 20, -1);
c.red = (gushort)(color[0]*0xFFFF);
c.green = (gushort)(color[1]*0xFFFF);
c.blue = (gushort)(color[2]*0xFFFF);
gdk_color_alloc (gtk_widget_get_colormap(widget), &c);
gdk_gc_set_foreground(gc, &c);
gdk_draw_rectangle (pixmap, gc, TRUE, 0, 0,
widget->allocation.width - 20, widget->allocation.height - 20);
GtkWidget* pixmapwid = gtk_pixmap_new (pixmap, (GdkBitmap*)NULL);
gtk_widget_show (pixmapwid);
gtk_container_remove (GTK_CONTAINER(widget), GTK_BIN(widget)->child);
gtk_container_add (GTK_CONTAINER(widget), pixmapwid);
gdk_gc_destroy(gc);
}
void set_button_pixmap2 (GtkWidget* widget, unsigned char* color)
{
GdkColor c;
GdkGC* gc;
GdkPixmap* pixmap;
if (widget->window == NULL)
return;
if ((widget->allocation.width < 10) || (widget->allocation.height < 10))
return;
gc = gdk_gc_new (widget->window);
pixmap = gdk_pixmap_new (widget->window, widget->allocation.width - 10,
widget->allocation.height - 10, -1);
c.red = color[0]*256;
c.green = color[1]*256;
c.blue = color[2]*256;
gdk_color_alloc (gtk_widget_get_colormap(widget), &c);
gdk_gc_set_foreground(gc, &c);
gdk_draw_rectangle (pixmap, gc, TRUE, 0, 0,
widget->allocation.width - 5, widget->allocation.height - 5);
GtkWidget* pixmapwid = gtk_pixmap_new (pixmap, (GdkBitmap*)NULL);
gtk_widget_show (pixmapwid);
gtk_container_remove (GTK_CONTAINER(widget), GTK_BIN(widget)->child);
gtk_container_add (GTK_CONTAINER(widget), pixmapwid);
gdk_gc_destroy(gc);
}

12
linux/gtktools.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef _GTKTOOLS_H_
#define _GTKTOOLS_H_
GtkWidget* new_pixmap(GtkWidget* widget, const char** data);
GtkWidget* clist_title_with_arrow(GtkWidget* clist, char col, const char* label_text);
void set_notebook_tab (GtkWidget *notebook, gint page_num, GtkWidget *widget);
void set_button_pixmap (GtkWidget* widget, float* color);
void set_button_pixmap2 (GtkWidget* widget, unsigned char* color);
#endif // _GTKTOOLS_H_

17
linux/lc Normal file
View file

@ -0,0 +1,17 @@
#!/bin/sh
# set the LeoCAD library path
LEOCAD_LIB=/mnt/f/projects/leocad/windows/debug/
export LEOCAD_LIB
# run the program
./leocad

Some files were not shown because too many files have changed in this diff Show more