From 1f9a1761c02486b325d00f54a759818e04567a06 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 7 Sep 2011 21:06:51 +0000 Subject: [PATCH] Copied 0.75 branch as the new trunk. --- CREDITS.txt | 11 + Makefile | 152 + README.txt | 61 + common/Makefile | 1 + common/algebra.cpp | 433 + common/algebra.h | 707 ++ common/array.cpp | 341 + common/array.h | 70 + common/basewnd.h | 102 + common/camera.cpp | 852 ++ common/camera.h | 195 + common/console.cpp | 93 + common/console.h | 42 + common/curve.cpp | 872 ++ common/curve.h | 127 + common/debug.cpp | 100 + common/debug.h | 17 + common/defines.h | 218 + common/file.cpp | 571 ++ common/file.h | 140 + common/globals.cpp | 89 + common/globals.h | 19 + common/glwindow.h | 52 + common/group.cpp | 79 + common/group.h | 31 + common/im_bmp.cpp | 492 ++ common/im_gif.cpp | 699 ++ common/im_jpg.cpp | 401 + common/im_png.cpp | 375 + common/image.cpp | 512 ++ common/image.h | 54 + common/keyboard.cpp | 433 + common/keyboard.h | 151 + common/lc_application.cpp | 407 + common/lc_application.h | 51 + common/library.cpp | 2749 ++++++ common/library.h | 168 + common/light.cpp | 573 ++ common/light.h | 138 + common/mainwnd.cpp | 101 + common/mainwnd.h | 34 + common/matrix.cpp | 688 ++ common/matrix.h | 49 + common/message.cpp | 32 + common/message.h | 40 + common/minifig.cpp | 1369 +++ common/minifig.h | 73 + common/module.mk | 22 + common/object.cpp | 568 ++ common/object.h | 213 + common/opengl.cpp | 1549 ++++ common/opengl.h | 1274 +++ common/piece.cpp | 1883 +++++ common/piece.h | 155 + common/pieceinf.cpp | 1946 +++++ common/pieceinf.h | 130 + common/preview.cpp | 185 + common/preview.h | 42 + common/project.cpp | 9338 +++++++++++++++++++++ common/project.h | 313 + common/quant.cpp | 640 ++ common/quant.h | 31 + common/str.cpp | 402 + common/str.h | 157 + common/system.h | 122 + common/terrain.cpp | 861 ++ common/terrain.h | 83 + common/texfont.cpp | 160 + common/texfont.h | 38 + common/texture.cpp | 271 + common/texture.h | 53 + common/tr.cpp | 325 + common/tr.h | 75 + common/typedefs.h | 420 + common/vector.cpp | 105 + common/vector.h | 30 + common/view.cpp | 139 + common/view.h | 33 + config.mk | 385 + description-pak | 5 + docs/CHANGES.txt | 206 + docs/COPYING.txt | 340 + docs/INSTALL.txt | 26 + docs/LINUX.txt | 89 + docs/TODO.txt | 24 + docs/leocad.1 | 118 + fixwin.sh | 21 + generic.mk | 11 + linux/Makefile | 1 + linux/basewnd.cpp | 247 + linux/dialogs.cpp | 3193 +++++++ linux/dialogs.h | 40 + linux/dlgfile.cpp | 345 + linux/dlgpiece.cpp | 408 + linux/glwindow.cpp | 280 + linux/gtkmisc.cpp | 229 + linux/gtkmisc.h | 25 + linux/gtktools.cpp | 114 + linux/gtktools.h | 12 + linux/lc | 17 + linux/linux_gl.cpp | 159 + linux/linux_gl.h | 114 + linux/main.cpp | 762 ++ linux/main.h | 81 + linux/menu.cpp | 376 + linux/module.mk | 5 + linux/pixmaps/ac-brick.xpm | 25 + linux/pixmaps/ac-cam.xpm | 24 + linux/pixmaps/ac-erase.xpm | 26 + linux/pixmaps/ac-light.xpm | 23 + linux/pixmaps/ac-move.xpm | 25 + linux/pixmaps/ac-next.xpm | 27 + linux/pixmaps/ac-paint.xpm | 25 + linux/pixmaps/ac-pan.xpm | 23 + linux/pixmaps/ac-prev.xpm | 27 + linux/pixmaps/ac-roll.xpm | 26 + linux/pixmaps/ac-rot.xpm | 25 + linux/pixmaps/ac-rotv.xpm | 25 + linux/pixmaps/ac-sel.xpm | 23 + linux/pixmaps/ac-spot.xpm | 24 + linux/pixmaps/ac-zoom.xpm | 25 + linux/pixmaps/ac-zoome.xpm | 27 + linux/pixmaps/ac-zoomr.xpm | 27 + linux/pixmaps/an-anim.xpm | 23 + linux/pixmaps/an-first.xpm | 25 + linux/pixmaps/an-key.xpm | 27 + linux/pixmaps/an-last.xpm | 25 + linux/pixmaps/an-next.xpm | 25 + linux/pixmaps/an-play.xpm | 26 + linux/pixmaps/an-prev.xpm | 25 + linux/pixmaps/an-stop.xpm | 26 + linux/pixmaps/cr_brick.xpm | 41 + linux/pixmaps/cr_cam.xpm | 41 + linux/pixmaps/cr_erase.xpm | 41 + linux/pixmaps/cr_light.xpm | 41 + linux/pixmaps/cr_move.xpm | 41 + linux/pixmaps/cr_paint.xpm | 41 + linux/pixmaps/cr_pan.xpm | 41 + linux/pixmaps/cr_roll.xpm | 41 + linux/pixmaps/cr_rot.xpm | 41 + linux/pixmaps/cr_rotv.xpm | 41 + linux/pixmaps/cr_sel.xpm | 41 + linux/pixmaps/cr_selm.xpm | 43 + linux/pixmaps/cr_spot.xpm | 41 + linux/pixmaps/cr_zoom.xpm | 42 + linux/pixmaps/cr_zoomr.xpm | 41 + linux/pixmaps/icon32.xpm | 41 + linux/pixmaps/info.xpm | 26 + linux/pixmaps/photo.xpm | 28 + linux/pixmaps/pi-acces.xpm | 24 + linux/pixmaps/pi-brick.xpm | 24 + linux/pixmaps/pi-extra.xpm | 25 + linux/pixmaps/pi-misc.xpm | 23 + linux/pixmaps/pi-plate.xpm | 24 + linux/pixmaps/pi-slope.xpm | 24 + linux/pixmaps/pi-space.xpm | 25 + linux/pixmaps/pi-tech.xpm | 26 + linux/pixmaps/pi-tile.xpm | 24 + linux/pixmaps/pi-train.xpm | 25 + linux/pixmaps/st-about.xpm | 24 + linux/pixmaps/st-bg.xpm | 23 + linux/pixmaps/st-copy.xpm | 25 + linux/pixmaps/st-cut.xpm | 24 + linux/pixmaps/st-fast.xpm | 21 + linux/pixmaps/st-help.xpm | 25 + linux/pixmaps/st-lock.xpm | 34 + linux/pixmaps/st-new.xpm | 24 + linux/pixmaps/st-open.xpm | 26 + linux/pixmaps/st-paste.xpm | 29 + linux/pixmaps/st-prev.xpm | 24 + linux/pixmaps/st-print.xpm | 24 + linux/pixmaps/st-redo.xpm | 24 + linux/pixmaps/st-save.xpm | 25 + linux/pixmaps/st-snap.xpm | 27 + linux/pixmaps/st-snapa.xpm | 23 + linux/pixmaps/st-undo.xpm | 24 + linux/pixmaps/vports01.xpm | 36 + linux/pixmaps/vports02.xpm | 37 + linux/pixmaps/vports03.xpm | 37 + linux/pixmaps/vports04.xpm | 37 + linux/pixmaps/vports05.xpm | 37 + linux/pixmaps/vports06.xpm | 37 + linux/pixmaps/vports07.xpm | 37 + linux/pixmaps/vports08.xpm | 37 + linux/pixmaps/vports09.xpm | 37 + linux/pixmaps/vports10.xpm | 37 + linux/pixmaps/vports11.xpm | 37 + linux/pixmaps/vports12.xpm | 37 + linux/pixmaps/vports13.xpm | 37 + linux/pixmaps/vports14.xpm | 37 + linux/profile.cpp | 195 + linux/system.cpp | 761 ++ linux/toolbar.cpp | 1010 +++ linux/toolbar.h | 81 + macosx/English.lproj/InfoPlist.strings | Bin 0 -> 254 bytes macosx/English.lproj/main.nib/classes.nib | 4 + macosx/English.lproj/main.nib/info.nib | 25 + macosx/English.lproj/main.nib/objects.xib | 327 + macosx/Info.plist | 24 + macosx/Makefile | 1 + macosx/PkgInfo | 1 + macosx/basewnd.cpp | 41 + macosx/config.mk | 9 + macosx/glwindow.cpp | 282 + macosx/main.cpp | 143 + macosx/module.mk | 2 + macosx/osx_gl.cpp | 34 + macosx/system.cpp | 233 + tools/convert/convert.c | 1720 ++++ tools/convert/convert.h | 221 + tools/convert/library.txt | 101 + tools/convert/moved.txt | 365 + tools/convert/texture.cpp | 120 + tools/convert/texture.h | 678 ++ tools/setup/leocad.nsi | 107 + tools/setup/setup.ico | Bin 0 -> 1078 bytes tools/update/update.cpp | 323 + tools/update/update.dsp | 100 + tools/update/update.dsw | 29 + tools/update/update.h | 6 + version.mk | 5 + win/3dsftk/3dsftk.dsp | 424 + win/3dsftk/3dsftk.vcproj | 1161 +++ win/3dsftk/source/swapbyte.c | 36 + win/Bmpmenu.cpp | 1543 ++++ win/Bmpmenu.h | 171 + win/Caddoc.cpp | 38 + win/Caddoc.h | 36 + win/Cadview.cpp | 1039 +++ win/Cadview.h | 89 + win/Clrpick.cpp | 231 + win/Clrpopup.cpp | 839 ++ win/Colorlst.cpp | 135 + win/Colorlst.h | 56 + win/EdGrpDlg.cpp | 105 + win/EdGrpDlg.h | 50 + win/Figdlg.cpp | 167 + win/Figdlg.h | 71 + win/Flatbar.cpp | 674 ++ win/Groupdlg.cpp | 50 + win/GrpTree.cpp | 263 + win/GrpTree.h | 64 + win/Htmldlg.cpp | 105 + win/Htmldlg.h | 58 + win/Imagedlg.cpp | 118 + win/Imagedlg.h | 61 + win/LeoCAD.dsp | 1320 +++ win/LeoCAD.rc | 1911 +++++ win/LeoCAD.vcproj | 3107 +++++++ win/Leocad.cpp | 315 + win/Leocad.dsw | 89 + win/Leocad.h | 59 + win/Leocad.sln | 47 + win/Libdlg.cpp | 544 ++ win/Libdlg.h | 58 + win/Mainfrm.cpp | 1428 ++++ win/Mainfrm.h | 113 + win/Moddlg.cpp | 450 + win/Moddlg.h | 94 + win/Piecebar.cpp | 1152 +++ win/Piecebar.h | 132 + win/Piececmb.cpp | 153 + win/Pieceprv.cpp | 93 + win/Pieceprv.h | 56 + win/Povdlg.cpp | 137 + win/Prefpage.cpp | 986 +++ win/Prefpage.h | 325 + win/Prefsht.cpp | 125 + win/Print.cpp | 808 ++ win/Propspgs.cpp | 324 + win/Propspgs.h | 131 + win/Seldlg.cpp | 267 + win/Seldlg.h | 60 + win/Stdafx.h | 66 + win/Stepdlg.cpp | 159 + win/Stepdlg.h | 58 + win/Steppop.cpp | 89 + win/Steppop.h | 53 + win/System.cpp | 1825 ++++ win/Teropdlg.cpp | 122 + win/Teropdlg.h | 60 + win/Terrdlg.cpp | 264 + win/Terrdlg.h | 66 + win/Terrwnd.cpp | 271 + win/Terrwnd.h | 73 + win/Tools.cpp | 790 ++ win/Tools.h | 14 + win/aboutdlg.cpp | 204 + win/aboutdlg.h | 50 + win/arraydlg.cpp | 133 + win/arraydlg.h | 65 + win/barcmdui.cpp | 79 + win/barcmdui.h | 12 + win/basewnd.cpp | 56 + win/cadbar.cpp | 143 + win/cadbar.h | 56 + win/categdlg.cpp | 45 + win/categdlg.h | 47 + win/clrpick.h | 75 + win/clrpopup.h | 97 + win/config.h | 37 + win/disabtab.cpp | 233 + win/disabtab.h | 40 + win/flatbar.h | 54 + win/glwindow.cpp | 267 + win/groupdlg.h | 45 + win/hlp/afxcore.rtf | 385 + win/hlp/afxprint.rtf | 98 + win/hlp/appexit.bmp | Bin 0 -> 2262 bytes win/hlp/bullet.bmp | Bin 0 -> 142 bytes win/hlp/commands.rtf | 280 + win/hlp/curarw2.bmp | Bin 0 -> 310 bytes win/hlp/curarw4.bmp | Bin 0 -> 566 bytes win/hlp/curhelp.bmp | Bin 0 -> 502 bytes win/hlp/dialogs.rtf | 225 + win/hlp/editcopy.bmp | Bin 0 -> 502 bytes win/hlp/editcut.bmp | Bin 0 -> 502 bytes win/hlp/editpast.bmp | Bin 0 -> 502 bytes win/hlp/editundo.bmp | Bin 0 -> 502 bytes win/hlp/filenew.bmp | Bin 0 -> 566 bytes win/hlp/fileopen.bmp | Bin 0 -> 566 bytes win/hlp/fileprnt.bmp | Bin 0 -> 502 bytes win/hlp/filesave.bmp | Bin 0 -> 502 bytes win/hlp/hlpsbar.bmp | Bin 0 -> 8068 bytes win/hlp/hlptbar.bmp | Bin 0 -> 2354 bytes win/hlp/leocad.cnt | 9 + win/hlp/leocad.hpj | 73 + win/hlp/leocad.log | 366 + win/hlp/leocad.ph | 327 + win/hlp/misc.rtf | 733 ++ win/hlp/popups.rtf | 144 + win/hlp/recfirst.bmp | Bin 0 -> 502 bytes win/hlp/reclast.bmp | Bin 0 -> 502 bytes win/hlp/recnext.bmp | Bin 0 -> 502 bytes win/hlp/recprev.bmp | Bin 0 -> 502 bytes win/hlp/scmax.bmp | Bin 0 -> 502 bytes win/hlp/scmenu.bmp | Bin 0 -> 2134 bytes win/hlp/scmin.bmp | Bin 0 -> 502 bytes win/hlp/titlebar.bmp | Bin 0 -> 13816 bytes win/ipedit.cpp | 225 + win/ipedit.h | 64 + win/jpeglib/jconfig.h | 45 + win/jpeglib/jpeglib.dsp | 312 + win/jpeglib/jpeglib.vcproj | 1081 +++ win/keyedit.cpp | 114 + win/keyedit.h | 56 + win/libpng/libpng.dsp | 156 + win/libpng/libpng.vcproj | 509 ++ win/makehelp.bat | 39 + win/piececmb.h | 56 + win/povdlg.h | 56 + win/prefsht.h | 62 + win/prevview.cpp | 1001 +++ win/prevview.h | 136 + win/print.h | 26 + win/prof.bat | 8 + win/progdlg.cpp | 184 + win/progdlg.h | 67 + win/propssht.cpp | 43 + win/propssht.h | 50 + win/res/Toolbar.bmp | Bin 0 -> 2158 bytes win/res/angle.cur | Bin 0 -> 326 bytes win/res/animator.bmp | Bin 0 -> 1078 bytes win/res/autopan.bmp | Bin 0 -> 534 bytes win/res/bitmap1.bmp | Bin 0 -> 478 bytes win/res/bitmap2.bmp | Bin 0 -> 238 bytes win/res/brick.cur | Bin 0 -> 766 bytes win/res/bulb.cur | Bin 0 -> 326 bytes win/res/caddoc.ico | Bin 0 -> 1078 bytes win/res/camera.bmp | Bin 0 -> 238 bytes win/res/camera.cur | Bin 0 -> 326 bytes win/res/cursor1.cur | Bin 0 -> 326 bytes win/res/delete.bmp | Bin 0 -> 238 bytes win/res/editor.bmp | Bin 0 -> 630 bytes win/res/eraser.cur | Bin 0 -> 766 bytes win/res/fullscr.bmp | Bin 0 -> 238 bytes win/res/fullscre.bmp | Bin 0 -> 238 bytes win/res/group.bmp | Bin 0 -> 238 bytes win/res/help.bmp | Bin 0 -> 238 bytes win/res/home.bmp | Bin 0 -> 238 bytes win/res/idr_main.ico | Bin 0 -> 1078 bytes win/res/idr_part.ico | Bin 0 -> 1078 bytes win/res/info.bmp | Bin 0 -> 238 bytes win/res/itoolbar.bmp | Bin 0 -> 718 bytes win/res/leocad.ico | Bin 0 -> 1078 bytes win/res/leocad.rc2 | 13 + win/res/library.bmp | Bin 0 -> 838 bytes win/res/light.bmp | Bin 0 -> 238 bytes win/res/light.cur | Bin 0 -> 326 bytes win/res/mail.bmp | Bin 0 -> 238 bytes win/res/move.cur | Bin 0 -> 766 bytes win/res/paint.cur | Bin 0 -> 766 bytes win/res/pan.cur | Bin 0 -> 326 bytes win/res/pan_ne.cur | Bin 0 -> 326 bytes win/res/pan_nw.cur | Bin 0 -> 326 bytes win/res/pan_se.cur | Bin 0 -> 326 bytes win/res/pan_sw.cur | Bin 0 -> 326 bytes win/res/panall.cur | Bin 0 -> 326 bytes win/res/pandown.cur | Bin 0 -> 326 bytes win/res/panleft.cur | Bin 0 -> 326 bytes win/res/panright.cur | Bin 0 -> 326 bytes win/res/panup.cur | Bin 0 -> 326 bytes win/res/particon.bmp | Bin 0 -> 630 bytes win/res/photo.bmp | Bin 0 -> 238 bytes win/res/piece.bmp | Bin 0 -> 238 bytes win/res/pieceed.bmp | Bin 0 -> 478 bytes win/res/preferen.bmp | Bin 0 -> 238 bytes win/res/preview.bmp | Bin 0 -> 1078 bytes win/res/roll.cur | Bin 0 -> 326 bytes win/res/rotate.cur | Bin 0 -> 766 bytes win/res/rotx.cur | Bin 0 -> 326 bytes win/res/roty.cur | Bin 0 -> 326 bytes win/res/selctgrp.cur | Bin 0 -> 326 bytes win/res/select.cur | Bin 0 -> 326 bytes win/res/split.cur | Bin 0 -> 326 bytes win/res/tab.bmp | Bin 0 -> 478 bytes win/res/terrain.bmp | Bin 0 -> 718 bytes win/res/toolsbar.bmp | Bin 0 -> 2398 bytes win/res/ungroup.bmp | Bin 0 -> 238 bytes win/res/views.bmp | Bin 0 -> 9046 bytes win/res/zoom.cur | Bin 0 -> 766 bytes win/res/zoomin.bmp | Bin 0 -> 238 bytes win/res/zoomout.bmp | Bin 0 -> 238 bytes win/res/zoomrect.cur | Bin 0 -> 766 bytes win/resource.h | 698 ++ win/resource.hm | 7 + win/rmodel.cpp | 1536 ++++ win/rmodel.h | 97 + win/splitter.cpp | 260 + win/splitter.h | 63 + win/stdafx.cpp | 6 + win/terrctrl.cpp | 1853 ++++ win/terrctrl.h | 153 + win/texdlg.cpp | 118 + win/texdlg.h | 50 + win/transdlg.cpp | 53 + win/transdlg.h | 51 + win/wheelwnd.cpp | 211 + win/wheelwnd.h | 61 + win/win_gl.cpp | 177 + win/win_gl.h | 101 + win/zlib/zlib.dsp | 184 + win/zlib/zlib.vcproj | 397 + 443 files changed, 94172 insertions(+) create mode 100644 CREDITS.txt create mode 100644 Makefile create mode 100644 README.txt create mode 100644 common/Makefile create mode 100644 common/algebra.cpp create mode 100644 common/algebra.h create mode 100755 common/array.cpp create mode 100755 common/array.h create mode 100644 common/basewnd.h create mode 100644 common/camera.cpp create mode 100644 common/camera.h create mode 100644 common/console.cpp create mode 100644 common/console.h create mode 100755 common/curve.cpp create mode 100755 common/curve.h create mode 100644 common/debug.cpp create mode 100644 common/debug.h create mode 100644 common/defines.h create mode 100644 common/file.cpp create mode 100644 common/file.h create mode 100644 common/globals.cpp create mode 100644 common/globals.h create mode 100644 common/glwindow.h create mode 100644 common/group.cpp create mode 100644 common/group.h create mode 100755 common/im_bmp.cpp create mode 100644 common/im_gif.cpp create mode 100644 common/im_jpg.cpp create mode 100755 common/im_png.cpp create mode 100644 common/image.cpp create mode 100644 common/image.h create mode 100644 common/keyboard.cpp create mode 100644 common/keyboard.h create mode 100644 common/lc_application.cpp create mode 100644 common/lc_application.h create mode 100755 common/library.cpp create mode 100755 common/library.h create mode 100644 common/light.cpp create mode 100644 common/light.h create mode 100644 common/mainwnd.cpp create mode 100644 common/mainwnd.h create mode 100644 common/matrix.cpp create mode 100644 common/matrix.h create mode 100644 common/message.cpp create mode 100644 common/message.h create mode 100644 common/minifig.cpp create mode 100644 common/minifig.h create mode 100644 common/module.mk create mode 100755 common/object.cpp create mode 100755 common/object.h create mode 100755 common/opengl.cpp create mode 100755 common/opengl.h create mode 100644 common/piece.cpp create mode 100644 common/piece.h create mode 100644 common/pieceinf.cpp create mode 100644 common/pieceinf.h create mode 100644 common/preview.cpp create mode 100644 common/preview.h create mode 100644 common/project.cpp create mode 100644 common/project.h create mode 100644 common/quant.cpp create mode 100644 common/quant.h create mode 100644 common/str.cpp create mode 100644 common/str.h create mode 100755 common/system.h create mode 100644 common/terrain.cpp create mode 100644 common/terrain.h create mode 100644 common/texfont.cpp create mode 100644 common/texfont.h create mode 100644 common/texture.cpp create mode 100644 common/texture.h create mode 100644 common/tr.cpp create mode 100644 common/tr.h create mode 100644 common/typedefs.h create mode 100644 common/vector.cpp create mode 100644 common/vector.h create mode 100644 common/view.cpp create mode 100644 common/view.h create mode 100644 config.mk create mode 100644 description-pak create mode 100644 docs/CHANGES.txt create mode 100644 docs/COPYING.txt create mode 100644 docs/INSTALL.txt create mode 100644 docs/LINUX.txt create mode 100644 docs/TODO.txt create mode 100644 docs/leocad.1 create mode 100755 fixwin.sh create mode 100644 generic.mk create mode 100644 linux/Makefile create mode 100644 linux/basewnd.cpp create mode 100644 linux/dialogs.cpp create mode 100644 linux/dialogs.h create mode 100755 linux/dlgfile.cpp create mode 100755 linux/dlgpiece.cpp create mode 100644 linux/glwindow.cpp create mode 100755 linux/gtkmisc.cpp create mode 100755 linux/gtkmisc.h create mode 100644 linux/gtktools.cpp create mode 100644 linux/gtktools.h create mode 100644 linux/lc create mode 100755 linux/linux_gl.cpp create mode 100755 linux/linux_gl.h create mode 100644 linux/main.cpp create mode 100644 linux/main.h create mode 100644 linux/menu.cpp create mode 100644 linux/module.mk create mode 100644 linux/pixmaps/ac-brick.xpm create mode 100644 linux/pixmaps/ac-cam.xpm create mode 100644 linux/pixmaps/ac-erase.xpm create mode 100644 linux/pixmaps/ac-light.xpm create mode 100644 linux/pixmaps/ac-move.xpm create mode 100644 linux/pixmaps/ac-next.xpm create mode 100644 linux/pixmaps/ac-paint.xpm create mode 100644 linux/pixmaps/ac-pan.xpm create mode 100644 linux/pixmaps/ac-prev.xpm create mode 100644 linux/pixmaps/ac-roll.xpm create mode 100644 linux/pixmaps/ac-rot.xpm create mode 100644 linux/pixmaps/ac-rotv.xpm create mode 100644 linux/pixmaps/ac-sel.xpm create mode 100644 linux/pixmaps/ac-spot.xpm create mode 100644 linux/pixmaps/ac-zoom.xpm create mode 100644 linux/pixmaps/ac-zoome.xpm create mode 100644 linux/pixmaps/ac-zoomr.xpm create mode 100644 linux/pixmaps/an-anim.xpm create mode 100644 linux/pixmaps/an-first.xpm create mode 100644 linux/pixmaps/an-key.xpm create mode 100644 linux/pixmaps/an-last.xpm create mode 100644 linux/pixmaps/an-next.xpm create mode 100644 linux/pixmaps/an-play.xpm create mode 100644 linux/pixmaps/an-prev.xpm create mode 100644 linux/pixmaps/an-stop.xpm create mode 100644 linux/pixmaps/cr_brick.xpm create mode 100644 linux/pixmaps/cr_cam.xpm create mode 100644 linux/pixmaps/cr_erase.xpm create mode 100644 linux/pixmaps/cr_light.xpm create mode 100644 linux/pixmaps/cr_move.xpm create mode 100644 linux/pixmaps/cr_paint.xpm create mode 100644 linux/pixmaps/cr_pan.xpm create mode 100644 linux/pixmaps/cr_roll.xpm create mode 100644 linux/pixmaps/cr_rot.xpm create mode 100644 linux/pixmaps/cr_rotv.xpm create mode 100644 linux/pixmaps/cr_sel.xpm create mode 100644 linux/pixmaps/cr_selm.xpm create mode 100644 linux/pixmaps/cr_spot.xpm create mode 100644 linux/pixmaps/cr_zoom.xpm create mode 100644 linux/pixmaps/cr_zoomr.xpm create mode 100644 linux/pixmaps/icon32.xpm create mode 100644 linux/pixmaps/info.xpm create mode 100644 linux/pixmaps/photo.xpm create mode 100644 linux/pixmaps/pi-acces.xpm create mode 100644 linux/pixmaps/pi-brick.xpm create mode 100644 linux/pixmaps/pi-extra.xpm create mode 100644 linux/pixmaps/pi-misc.xpm create mode 100644 linux/pixmaps/pi-plate.xpm create mode 100644 linux/pixmaps/pi-slope.xpm create mode 100644 linux/pixmaps/pi-space.xpm create mode 100644 linux/pixmaps/pi-tech.xpm create mode 100644 linux/pixmaps/pi-tile.xpm create mode 100644 linux/pixmaps/pi-train.xpm create mode 100644 linux/pixmaps/st-about.xpm create mode 100644 linux/pixmaps/st-bg.xpm create mode 100644 linux/pixmaps/st-copy.xpm create mode 100644 linux/pixmaps/st-cut.xpm create mode 100644 linux/pixmaps/st-fast.xpm create mode 100644 linux/pixmaps/st-help.xpm create mode 100755 linux/pixmaps/st-lock.xpm create mode 100644 linux/pixmaps/st-new.xpm create mode 100644 linux/pixmaps/st-open.xpm create mode 100644 linux/pixmaps/st-paste.xpm create mode 100644 linux/pixmaps/st-prev.xpm create mode 100644 linux/pixmaps/st-print.xpm create mode 100644 linux/pixmaps/st-redo.xpm create mode 100644 linux/pixmaps/st-save.xpm create mode 100644 linux/pixmaps/st-snap.xpm create mode 100644 linux/pixmaps/st-snapa.xpm create mode 100644 linux/pixmaps/st-undo.xpm create mode 100755 linux/pixmaps/vports01.xpm create mode 100755 linux/pixmaps/vports02.xpm create mode 100755 linux/pixmaps/vports03.xpm create mode 100755 linux/pixmaps/vports04.xpm create mode 100755 linux/pixmaps/vports05.xpm create mode 100755 linux/pixmaps/vports06.xpm create mode 100755 linux/pixmaps/vports07.xpm create mode 100755 linux/pixmaps/vports08.xpm create mode 100755 linux/pixmaps/vports09.xpm create mode 100755 linux/pixmaps/vports10.xpm create mode 100755 linux/pixmaps/vports11.xpm create mode 100755 linux/pixmaps/vports12.xpm create mode 100755 linux/pixmaps/vports13.xpm create mode 100755 linux/pixmaps/vports14.xpm create mode 100755 linux/profile.cpp create mode 100644 linux/system.cpp create mode 100644 linux/toolbar.cpp create mode 100644 linux/toolbar.h create mode 100644 macosx/English.lproj/InfoPlist.strings create mode 100644 macosx/English.lproj/main.nib/classes.nib create mode 100644 macosx/English.lproj/main.nib/info.nib create mode 100644 macosx/English.lproj/main.nib/objects.xib create mode 100644 macosx/Info.plist create mode 100644 macosx/Makefile create mode 100644 macosx/PkgInfo create mode 100644 macosx/basewnd.cpp create mode 100644 macosx/config.mk create mode 100755 macosx/glwindow.cpp create mode 100755 macosx/main.cpp create mode 100644 macosx/module.mk create mode 100755 macosx/osx_gl.cpp create mode 100644 macosx/system.cpp create mode 100644 tools/convert/convert.c create mode 100644 tools/convert/convert.h create mode 100755 tools/convert/library.txt create mode 100644 tools/convert/moved.txt create mode 100644 tools/convert/texture.cpp create mode 100644 tools/convert/texture.h create mode 100644 tools/setup/leocad.nsi create mode 100644 tools/setup/setup.ico create mode 100755 tools/update/update.cpp create mode 100755 tools/update/update.dsp create mode 100755 tools/update/update.dsw create mode 100755 tools/update/update.h create mode 100644 version.mk create mode 100644 win/3dsftk/3dsftk.dsp create mode 100644 win/3dsftk/3dsftk.vcproj create mode 100644 win/3dsftk/source/swapbyte.c create mode 100644 win/Bmpmenu.cpp create mode 100644 win/Bmpmenu.h create mode 100644 win/Caddoc.cpp create mode 100644 win/Caddoc.h create mode 100644 win/Cadview.cpp create mode 100644 win/Cadview.h create mode 100644 win/Clrpick.cpp create mode 100644 win/Clrpopup.cpp create mode 100644 win/Colorlst.cpp create mode 100644 win/Colorlst.h create mode 100644 win/EdGrpDlg.cpp create mode 100644 win/EdGrpDlg.h create mode 100644 win/Figdlg.cpp create mode 100644 win/Figdlg.h create mode 100644 win/Flatbar.cpp create mode 100644 win/Groupdlg.cpp create mode 100644 win/GrpTree.cpp create mode 100644 win/GrpTree.h create mode 100644 win/Htmldlg.cpp create mode 100644 win/Htmldlg.h create mode 100644 win/Imagedlg.cpp create mode 100644 win/Imagedlg.h create mode 100644 win/LeoCAD.dsp create mode 100644 win/LeoCAD.rc create mode 100644 win/LeoCAD.vcproj create mode 100644 win/Leocad.cpp create mode 100644 win/Leocad.dsw create mode 100644 win/Leocad.h create mode 100644 win/Leocad.sln create mode 100644 win/Libdlg.cpp create mode 100644 win/Libdlg.h create mode 100644 win/Mainfrm.cpp create mode 100644 win/Mainfrm.h create mode 100644 win/Moddlg.cpp create mode 100644 win/Moddlg.h create mode 100644 win/Piecebar.cpp create mode 100644 win/Piecebar.h create mode 100644 win/Piececmb.cpp create mode 100644 win/Pieceprv.cpp create mode 100644 win/Pieceprv.h create mode 100644 win/Povdlg.cpp create mode 100644 win/Prefpage.cpp create mode 100644 win/Prefpage.h create mode 100644 win/Prefsht.cpp create mode 100644 win/Print.cpp create mode 100644 win/Propspgs.cpp create mode 100644 win/Propspgs.h create mode 100644 win/Seldlg.cpp create mode 100644 win/Seldlg.h create mode 100644 win/Stdafx.h create mode 100644 win/Stepdlg.cpp create mode 100644 win/Stepdlg.h create mode 100644 win/Steppop.cpp create mode 100644 win/Steppop.h create mode 100644 win/System.cpp create mode 100644 win/Teropdlg.cpp create mode 100644 win/Teropdlg.h create mode 100644 win/Terrdlg.cpp create mode 100644 win/Terrdlg.h create mode 100644 win/Terrwnd.cpp create mode 100644 win/Terrwnd.h create mode 100644 win/Tools.cpp create mode 100644 win/Tools.h create mode 100644 win/aboutdlg.cpp create mode 100644 win/aboutdlg.h create mode 100644 win/arraydlg.cpp create mode 100644 win/arraydlg.h create mode 100644 win/barcmdui.cpp create mode 100644 win/barcmdui.h create mode 100644 win/basewnd.cpp create mode 100644 win/cadbar.cpp create mode 100644 win/cadbar.h create mode 100644 win/categdlg.cpp create mode 100644 win/categdlg.h create mode 100644 win/clrpick.h create mode 100644 win/clrpopup.h create mode 100644 win/config.h create mode 100644 win/disabtab.cpp create mode 100644 win/disabtab.h create mode 100644 win/flatbar.h create mode 100644 win/glwindow.cpp create mode 100644 win/groupdlg.h create mode 100644 win/hlp/afxcore.rtf create mode 100644 win/hlp/afxprint.rtf create mode 100644 win/hlp/appexit.bmp create mode 100644 win/hlp/bullet.bmp create mode 100644 win/hlp/commands.rtf create mode 100644 win/hlp/curarw2.bmp create mode 100644 win/hlp/curarw4.bmp create mode 100644 win/hlp/curhelp.bmp create mode 100644 win/hlp/dialogs.rtf create mode 100644 win/hlp/editcopy.bmp create mode 100644 win/hlp/editcut.bmp create mode 100644 win/hlp/editpast.bmp create mode 100644 win/hlp/editundo.bmp create mode 100644 win/hlp/filenew.bmp create mode 100644 win/hlp/fileopen.bmp create mode 100644 win/hlp/fileprnt.bmp create mode 100644 win/hlp/filesave.bmp create mode 100644 win/hlp/hlpsbar.bmp create mode 100644 win/hlp/hlptbar.bmp create mode 100644 win/hlp/leocad.cnt create mode 100644 win/hlp/leocad.hpj create mode 100644 win/hlp/leocad.log create mode 100644 win/hlp/leocad.ph create mode 100644 win/hlp/misc.rtf create mode 100644 win/hlp/popups.rtf create mode 100644 win/hlp/recfirst.bmp create mode 100644 win/hlp/reclast.bmp create mode 100644 win/hlp/recnext.bmp create mode 100644 win/hlp/recprev.bmp create mode 100644 win/hlp/scmax.bmp create mode 100644 win/hlp/scmenu.bmp create mode 100644 win/hlp/scmin.bmp create mode 100644 win/hlp/titlebar.bmp create mode 100644 win/ipedit.cpp create mode 100644 win/ipedit.h create mode 100644 win/jpeglib/jconfig.h create mode 100644 win/jpeglib/jpeglib.dsp create mode 100644 win/jpeglib/jpeglib.vcproj create mode 100644 win/keyedit.cpp create mode 100644 win/keyedit.h create mode 100755 win/libpng/libpng.dsp create mode 100644 win/libpng/libpng.vcproj create mode 100644 win/makehelp.bat create mode 100644 win/piececmb.h create mode 100644 win/povdlg.h create mode 100644 win/prefsht.h create mode 100644 win/prevview.cpp create mode 100644 win/prevview.h create mode 100644 win/print.h create mode 100644 win/prof.bat create mode 100644 win/progdlg.cpp create mode 100644 win/progdlg.h create mode 100644 win/propssht.cpp create mode 100644 win/propssht.h create mode 100644 win/res/Toolbar.bmp create mode 100644 win/res/angle.cur create mode 100644 win/res/animator.bmp create mode 100644 win/res/autopan.bmp create mode 100644 win/res/bitmap1.bmp create mode 100644 win/res/bitmap2.bmp create mode 100644 win/res/brick.cur create mode 100644 win/res/bulb.cur create mode 100644 win/res/caddoc.ico create mode 100644 win/res/camera.bmp create mode 100644 win/res/camera.cur create mode 100644 win/res/cursor1.cur create mode 100644 win/res/delete.bmp create mode 100644 win/res/editor.bmp create mode 100644 win/res/eraser.cur create mode 100644 win/res/fullscr.bmp create mode 100644 win/res/fullscre.bmp create mode 100644 win/res/group.bmp create mode 100644 win/res/help.bmp create mode 100644 win/res/home.bmp create mode 100644 win/res/idr_main.ico create mode 100644 win/res/idr_part.ico create mode 100644 win/res/info.bmp create mode 100644 win/res/itoolbar.bmp create mode 100644 win/res/leocad.ico create mode 100644 win/res/leocad.rc2 create mode 100644 win/res/library.bmp create mode 100644 win/res/light.bmp create mode 100644 win/res/light.cur create mode 100644 win/res/mail.bmp create mode 100644 win/res/move.cur create mode 100644 win/res/paint.cur create mode 100644 win/res/pan.cur create mode 100644 win/res/pan_ne.cur create mode 100644 win/res/pan_nw.cur create mode 100644 win/res/pan_se.cur create mode 100644 win/res/pan_sw.cur create mode 100644 win/res/panall.cur create mode 100644 win/res/pandown.cur create mode 100644 win/res/panleft.cur create mode 100644 win/res/panright.cur create mode 100644 win/res/panup.cur create mode 100644 win/res/particon.bmp create mode 100644 win/res/photo.bmp create mode 100644 win/res/piece.bmp create mode 100644 win/res/pieceed.bmp create mode 100644 win/res/preferen.bmp create mode 100644 win/res/preview.bmp create mode 100644 win/res/roll.cur create mode 100644 win/res/rotate.cur create mode 100644 win/res/rotx.cur create mode 100644 win/res/roty.cur create mode 100644 win/res/selctgrp.cur create mode 100644 win/res/select.cur create mode 100644 win/res/split.cur create mode 100644 win/res/tab.bmp create mode 100644 win/res/terrain.bmp create mode 100644 win/res/toolsbar.bmp create mode 100644 win/res/ungroup.bmp create mode 100644 win/res/views.bmp create mode 100644 win/res/zoom.cur create mode 100644 win/res/zoomin.bmp create mode 100644 win/res/zoomout.bmp create mode 100644 win/res/zoomrect.cur create mode 100644 win/resource.h create mode 100644 win/resource.hm create mode 100644 win/rmodel.cpp create mode 100644 win/rmodel.h create mode 100644 win/splitter.cpp create mode 100644 win/splitter.h create mode 100644 win/stdafx.cpp create mode 100644 win/terrctrl.cpp create mode 100644 win/terrctrl.h create mode 100644 win/texdlg.cpp create mode 100644 win/texdlg.h create mode 100644 win/transdlg.cpp create mode 100644 win/transdlg.h create mode 100644 win/wheelwnd.cpp create mode 100644 win/wheelwnd.h create mode 100755 win/win_gl.cpp create mode 100755 win/win_gl.h create mode 100755 win/zlib/zlib.dsp create mode 100644 win/zlib/zlib.vcproj diff --git a/CREDITS.txt b/CREDITS.txt new file mode 100644 index 00000000..23e189f0 --- /dev/null +++ b/CREDITS.txt @@ -0,0 +1,11 @@ +The following people have contributed to LeoCAD: + +Author, Original Design: +Leonardo Zide + +Contributions: +Christian Höltje +Renaud Breard + +Linux man page and Debian packages: +Pat Mahoney \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..8342fcbc --- /dev/null +++ b/Makefile @@ -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 '*/.#*') + diff --git a/README.txt b/README.txt new file mode 100644 index 00000000..e646ddcb --- /dev/null +++ b/README.txt @@ -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. diff --git a/common/Makefile b/common/Makefile new file mode 100644 index 00000000..05788fd5 --- /dev/null +++ b/common/Makefile @@ -0,0 +1 @@ +include ../generic.mk diff --git a/common/algebra.cpp b/common/algebra.cpp new file mode 100644 index 00000000..d90ef5d3 --- /dev/null +++ b/common/algebra.cpp @@ -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; +} diff --git a/common/algebra.h b/common/algebra.h new file mode 100644 index 00000000..0e2be09d --- /dev/null +++ b/common/algebra.h @@ -0,0 +1,707 @@ +#ifndef _ALGEBRA_H_ +#define _ALGEBRA_H_ + +#include + +// +// 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 + +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 diff --git a/common/array.cpp b/common/array.cpp new file mode 100755 index 00000000..a91a47fb --- /dev/null +++ b/common/array.cpp @@ -0,0 +1,341 @@ +// +// Simple array classes +// + +#include +#include + +template +PtrArray::PtrArray(int nSize) +{ + m_pData = NULL; + m_nLength = 0; + m_nAlloc = 0; + + if(nSize != 0) + Expand(nSize); +} + +template +PtrArray::~PtrArray() +{ + free(m_pData); +} + +template +void PtrArray::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 +void PtrArray::RemoveAll() +{ + m_nLength = 0; +} + +template +T* PtrArray::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 +T* PtrArray::RemovePointer(T* pObj) +{ + int i; + + for(i = 0; i < m_nLength; i++) + if(m_pData[i] == pObj) + return RemoveIndex(i); + + return NULL; +} + +template +void PtrArray::Add(T* pObj) +{ + Expand(1); + m_pData[m_nLength] = pObj; + m_nLength++; +} + +template +void PtrArray::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 +void PtrArray::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 +int PtrArray::FindIndex(T* Obj) const +{ + for (int i = 0; i < m_nLength; i++) + if (m_pData[i] == Obj) + return i; + + return -1; +} + +template +void PtrArray::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 +PtrArray& PtrArray::operator=(const PtrArray& 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 +PtrArray& PtrArray::operator+=(const PtrArray& 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 +ObjArray::ObjArray(int Size, int Grow) +{ + m_Data = NULL; + m_Length = 0; + m_Alloc = 0; + m_Grow = Grow; + + if (Size != 0) + Expand(Size); +} + +template +ObjArray::~ObjArray () +{ + delete[] m_Data; +} + +template +void ObjArray::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 +void ObjArray::RemoveAll() +{ + m_Length = 0; +} + +template +void ObjArray::RemoveIndex(int Index) +{ + m_Length--; + + for (int i = Index; i < m_Length; i++) + m_Data[i] = m_Data[i+1]; +} + +template +void ObjArray::Add(const T& Obj) +{ + Expand(1); + m_Data[m_Length++] = Obj; +} + +template +void ObjArray::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 +void ObjArray::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; +} +*/ diff --git a/common/array.h b/common/array.h new file mode 100755 index 00000000..2cb47643 --- /dev/null +++ b/common/array.h @@ -0,0 +1,70 @@ +#ifndef _ARRAY_H_ +#define _ARRAY_H_ + +template +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& operator=(const PtrArray& Array); + PtrArray& operator+=(const PtrArray& 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 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_ diff --git a/common/basewnd.h b/common/basewnd.h new file mode 100644 index 00000000..f26f4f57 --- /dev/null +++ b/common/basewnd.h @@ -0,0 +1,102 @@ +#ifndef _BASEWND_H_ +#define _BASEWND_H_ + +#include + +// 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 +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_ diff --git a/common/camera.cpp b/common/camera.cpp new file mode 100644 index 00000000..35d6194e --- /dev/null +++ b/common/camera.cpp @@ -0,0 +1,852 @@ +// Camera object. + +#include +#include +#include +#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; +} diff --git a/common/camera.h b/common/camera.h new file mode 100644 index 00000000..060086b2 --- /dev/null +++ b/common/camera.h @@ -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_ diff --git a/common/console.cpp b/common/console.cpp new file mode 100644 index 00000000..67a6113e --- /dev/null +++ b/common/console.cpp @@ -0,0 +1,93 @@ +// +// Debug Console +// + +#include +#include +#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); +} diff --git a/common/console.h b/common/console.h new file mode 100644 index 00000000..d3aac8a7 --- /dev/null +++ b/common/console.h @@ -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_ diff --git a/common/curve.cpp b/common/curve.cpp new file mode 100755 index 00000000..c9646a44 --- /dev/null +++ b/common/curve.cpp @@ -0,0 +1,872 @@ +// Curve class, used to represent all flexible objects +// + +#include +#include +#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 +/* +// 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 diff --git a/common/curve.h b/common/curve.h new file mode 100755 index 00000000..f9a90a6a --- /dev/null +++ b/common/curve.h @@ -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 m_Points; +}; + +#endif // _CURVE_H_ diff --git a/common/debug.cpp b/common/debug.cpp new file mode 100644 index 00000000..f4127cd0 --- /dev/null +++ b/common/debug.cpp @@ -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 diff --git a/common/debug.h b/common/debug.h new file mode 100644 index 00000000..90b65173 --- /dev/null +++ b/common/debug.h @@ -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_ diff --git a/common/defines.h b/common/defines.h new file mode 100644 index 00000000..c0f0f3a6 --- /dev/null +++ b/common/defines.h @@ -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 +#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_ diff --git a/common/file.cpp b/common/file.cpp new file mode 100644 index 00000000..23825ca3 --- /dev/null +++ b/common/file.cpp @@ -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 +#include +#include +#include +#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; +} diff --git a/common/file.h b/common/file.h new file mode 100644 index 00000000..cb583cd9 --- /dev/null +++ b/common/file.h @@ -0,0 +1,140 @@ +#ifndef _FILE_H_ +#define _FILE_H_ + +#include +#include +#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_ diff --git a/common/globals.cpp b/common/globals.cpp new file mode 100644 index 00000000..8a186efa --- /dev/null +++ b/common/globals.cpp @@ -0,0 +1,89 @@ +// +// Global variables common to all platforms. +// + +#include +#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 diff --git a/common/globals.h b/common/globals.h new file mode 100644 index 00000000..6f6daa16 --- /dev/null +++ b/common/globals.h @@ -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_ diff --git a/common/glwindow.h b/common/glwindow.h new file mode 100644 index 00000000..6fc3432e --- /dev/null +++ b/common/glwindow.h @@ -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_ diff --git a/common/group.cpp b/common/group.cpp new file mode 100644 index 00000000..89c6299a --- /dev/null +++ b/common/group.cpp @@ -0,0 +1,79 @@ +// Piece group +// + +#include +#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); +} diff --git a/common/group.h b/common/group.h new file mode 100644 index 00000000..45418e01 --- /dev/null +++ b/common/group.h @@ -0,0 +1,31 @@ +// +// group.h +//////////////////////////////////////////////////// + +#ifndef _GROUP_H +#define _GROUP_H + +class File; + +class Group +{ +public: +// void DoSaveLoad(CArchive& ar, CCADDoc* pDoc); + Group(); + ~Group(); + + void SetGroup(Group* pGroup); + void UnGroup(Group* pGroup); + Group* GetTopGroup(); + + Group* m_pNext; + Group* m_pGroup; + + void FileLoad(File* file); + void FileSave(File* file, Group* pGroups); + + char m_strName[65]; + float m_fCenter[3]; +}; + +#endif // _GROUP_H diff --git a/common/im_bmp.cpp b/common/im_bmp.cpp new file mode 100755 index 00000000..f850fa7d --- /dev/null +++ b/common/im_bmp.cpp @@ -0,0 +1,492 @@ +#include +#include +#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> 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; +} diff --git a/common/im_gif.cpp b/common/im_gif.cpp new file mode 100644 index 00000000..16ef6169 --- /dev/null +++ b/common/im_gif.cpp @@ -0,0 +1,699 @@ +#include +#include +#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<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; +} diff --git a/common/im_jpg.cpp b/common/im_jpg.cpp new file mode 100644 index 00000000..9da64b6f --- /dev/null +++ b/common/im_jpg.cpp @@ -0,0 +1,401 @@ +#include +#include +#include "config.h" +#include "image.h" +#include "file.h" + +#ifdef LC_HAVE_JPEGLIB + +extern "C" { +#include +} + +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 diff --git a/common/im_png.cpp b/common/im_png.cpp new file mode 100755 index 00000000..b4c9369a --- /dev/null +++ b/common/im_png.cpp @@ -0,0 +1,375 @@ +#include +#include "config.h" +#include "image.h" +#include "file.h" + +#ifdef LC_HAVE_PNGLIB + +#include + +#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 diff --git a/common/image.cpp b/common/image.cpp new file mode 100644 index 00000000..1accf6f5 --- /dev/null +++ b/common/image.cpp @@ -0,0 +1,512 @@ +// Image I/O routines +// + +#include "opengl.h" +#ifdef LC_WINDOWS +#include +#include +//#include +#include +#endif +#include +#include +#include +#include +#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<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<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<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 diff --git a/common/image.h b/common/image.h new file mode 100644 index 00000000..8bff6c53 --- /dev/null +++ b/common/image.h @@ -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_ diff --git a/common/keyboard.cpp b/common/keyboard.cpp new file mode 100644 index 00000000..ceff07c2 --- /dev/null +++ b/common/keyboard.cpp @@ -0,0 +1,433 @@ +// +// Code to handle user-defined keyboard shortcuts. +// + +#include +#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; +} diff --git a/common/keyboard.h b/common/keyboard.h new file mode 100644 index 00000000..9d43f4a7 --- /dev/null +++ b/common/keyboard.h @@ -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_ diff --git a/common/lc_application.cpp b/common/lc_application.cpp new file mode 100644 index 00000000..ef57f5dd --- /dev/null +++ b/common/lc_application.cpp @@ -0,0 +1,407 @@ +#include +#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 : Searches for OpenGL libraries in path.\n"); + printf(" --libpath : Loads the Pieces library from path.\n"); + printf(" -i, --image : Saves a picture in the format specified by ext.\n"); + printf(" -w, --width : Sets the picture width.\n"); + printf(" -h, --height : Sets the picture height.\n"); + printf(" -f, --from